├── .devcontainer
└── devcontainer.json
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .github
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── ci.yml
│ ├── codeql-analysis.yml
│ ├── npm-prepare-release.yml
│ ├── npm-publish.yml
│ └── stale.yml
├── .gitignore
├── .nvmrc
├── .prettierignore
├── .prettierrc
├── .storybook
├── decorators
│ ├── withBoundingBox.tsx
│ ├── withColorMode.tsx
│ └── withThemeProvider.tsx
├── main.ts
└── preview.tsx
├── README.md
├── babel.config.js
├── docs
├── ARCHITECTURE.md
├── CONTRIBUTING.md
├── RELEASING.md
├── SETUP.md
└── TESTING.md
├── package-lock.json
├── package.json
├── src
├── declaration.d.ts
└── system
│ ├── Accordion
│ ├── Accordion.stories.tsx
│ ├── Accordion.test.tsx
│ ├── Accordion.tsx
│ └── index.ts
│ ├── Avatar
│ ├── Avatar.stories.tsx
│ ├── Avatar.test.tsx
│ ├── Avatar.tsx
│ └── index.ts
│ ├── Badge
│ ├── Badge.stories.tsx
│ ├── Badge.test.tsx
│ ├── Badge.tsx
│ └── index.ts
│ ├── Box
│ ├── Box.stories.tsx
│ ├── Box.tsx
│ └── index.js
│ ├── Breadcrumbs
│ ├── Breadcrumbs.stories.tsx
│ ├── Breadcrumbs.test.tsx
│ ├── Breadcrumbs.tsx
│ └── styles.ts
│ ├── Button
│ ├── Button.stories.tsx
│ ├── Button.test.tsx
│ ├── Button.tsx
│ ├── ButtonSubmit.stories.tsx
│ ├── ButtonSubmit.test.tsx
│ ├── ButtonSubmit.tsx
│ └── index.ts
│ ├── Card
│ ├── Card.stories.tsx
│ ├── Card.test.tsx
│ ├── Card.tsx
│ └── index.ts
│ ├── Code
│ ├── Code.stories.tsx
│ ├── Code.test.tsx
│ ├── Code.tsx
│ └── index.ts
│ ├── ConfirmationDialog
│ ├── ConfirmationDialog.js
│ ├── ConfirmationDialog.stories.jsx
│ └── index.js
│ ├── Dialog
│ ├── Dialog.js
│ ├── Dialog.stories.jsx
│ ├── DialogButton.js
│ ├── DialogContent.js
│ ├── DialogDivider.js
│ ├── DialogMenu.js
│ ├── DialogMenuItem.js
│ ├── DialogTrigger.js
│ └── index.js
│ ├── Drawer
│ ├── Drawer.stories.tsx
│ ├── Drawer.test.tsx
│ ├── Drawer.tsx
│ └── styles.ts
│ ├── Dropdown
│ ├── Dropdown.stories.jsx
│ ├── Dropdown.test.tsx
│ ├── Dropdown.tsx
│ ├── DropdownContent.tsx
│ ├── DropdownItem.tsx
│ ├── DropdownLabel.tsx
│ ├── DropdownSeparator.tsx
│ └── index.ts
│ ├── FilterDropdown
│ ├── FilterDropdown.stories.tsx
│ ├── FilterDropdown.test.tsx
│ └── FilterDropdown.tsx
│ ├── Flex
│ ├── Flex.stories.tsx
│ ├── Flex.tsx
│ └── index.ts
│ ├── Footer
│ ├── Footer.stories.tsx
│ ├── Footer.test.tsx
│ └── Footer.tsx
│ ├── Form
│ ├── Checkbox
│ │ ├── Checkbox.stories.tsx
│ │ ├── Checkbox.test.tsx
│ │ ├── Checkbox.tsx
│ │ └── styles.ts
│ ├── Input.stories.tsx
│ ├── Input.styles.ts
│ ├── Input.tsx
│ ├── InputWithCopyButton.js
│ ├── InputWithCopyButton.stories.jsx
│ ├── Label.stories.tsx
│ ├── Label.tsx
│ ├── Radio
│ │ ├── Radio.stories.tsx
│ │ ├── Radio.test.tsx
│ │ ├── Radio.tsx
│ │ ├── RadioOption.tsx
│ │ └── styles.ts
│ ├── RadioBoxGroup.js
│ ├── RadioBoxGroup.stories.jsx
│ ├── RadioBoxGroup.test.jsx
│ ├── RadioGroupChip.stories.tsx
│ ├── RadioGroupChip.test.tsx
│ ├── RadioGroupChip.tsx
│ ├── RequiredLabel.tsx
│ ├── Textarea.js
│ ├── Textarea.stories.jsx
│ ├── Toggle.stories.tsx
│ ├── Toggle.test.tsx
│ ├── Toggle.tsx
│ ├── ToggleRow.js
│ ├── Validation.tsx
│ └── index.js
│ ├── Grid
│ ├── Grid.stories.tsx
│ ├── Grid.tsx
│ └── index.ts
│ ├── Heading
│ ├── Heading.stories.tsx
│ ├── Heading.tsx
│ └── index.js
│ ├── Hr
│ ├── Hr.stories.tsx
│ ├── Hr.test.tsx
│ └── Hr.tsx
│ ├── Link
│ ├── Link.stories.tsx
│ ├── Link.tsx
│ └── index.ts
│ ├── LinkExternal
│ ├── LinkExternal.stories.tsx
│ ├── LinkExternal.test.tsx
│ └── LinkExternal.tsx
│ ├── MobileMenu
│ ├── MobileMenu.stories.tsx
│ ├── MobileMenu.test.tsx
│ └── MobileMenu.tsx
│ ├── Nav
│ ├── Nav.stories.tsx
│ ├── Nav.test.tsx
│ ├── Nav.tsx
│ ├── NavItem.tsx
│ ├── NavItemGroup.test.tsx
│ ├── NavItemGroup.tsx
│ ├── styles.ts
│ └── styles
│ │ └── variants
│ │ ├── breadcrumbs.ts
│ │ ├── menu.ts
│ │ ├── menugroup.ts
│ │ ├── primary.ts
│ │ ├── tabs.ts
│ │ └── toolbar.ts
│ ├── NewConfirmationDialog
│ ├── NewConfirmationDialog.js
│ ├── NewConfirmationDialog.stories.jsx
│ ├── NewConfirmationDialog.test.js
│ └── index.js
│ ├── NewDialog
│ ├── DialogClose.test.tsx
│ ├── DialogClose.tsx
│ ├── DialogDescription.test.js
│ ├── DialogDescription.tsx
│ ├── DialogOverlay.test.js
│ ├── DialogOverlay.tsx
│ ├── DialogTitle.test.js
│ ├── DialogTitle.tsx
│ ├── NewDialog.stories.jsx
│ ├── NewDialog.tsx
│ ├── index.ts
│ └── styles.ts
│ ├── NewForm
│ ├── Fieldset.tsx
│ ├── Form.tsx
│ ├── FormAutocomplete.css
│ ├── FormAutocomplete.js
│ ├── FormAutocomplete.stories.jsx
│ ├── FormAutocomplete.test.js
│ ├── FormAutocompleteMultiselect.js
│ ├── FormAutocompleteMultiselect.stories.jsx
│ ├── FormAutocompleteMultiselect.test.js
│ ├── FormSelect.js
│ ├── FormSelect.stories.jsx
│ ├── FormSelect.test.js
│ ├── FormSelectArrow.js
│ ├── FormSelectContent.js
│ ├── FormSelectInline.js
│ ├── FormSelectLoading.js
│ ├── FormSelectSearch.js
│ ├── Legend.tsx
│ └── index.js
│ ├── Notice
│ ├── Notice.stories.tsx
│ ├── Notice.tsx
│ └── index.ts
│ ├── OptionRow
│ ├── OptionRow.js
│ ├── OptionRow.stories.jsx
│ ├── OptionRow.test.js
│ └── index.js
│ ├── Page
│ ├── Page.test.tsx
│ └── Page.tsx
│ ├── Progress
│ ├── Progress.stories.tsx
│ ├── Progress.test.tsx
│ ├── Progress.tsx
│ └── index.ts
│ ├── ScreenReaderText
│ ├── ScreenReader.test.js
│ ├── ScreenReaderText.tsx
│ └── index.js
│ ├── Skeleton
│ ├── Skeleton.stories.tsx
│ ├── Skeleton.tsx
│ └── index.ts
│ ├── Snackbar
│ ├── Snackbar.stories.tsx
│ ├── Snackbar.test.tsx
│ ├── Snackbar.tsx
│ └── index.ts
│ ├── Spinner
│ ├── Spinner.stories.tsx
│ ├── Spinner.test.tsx
│ ├── Spinner.tsx
│ └── index.ts
│ ├── Table
│ ├── Table.stories.tsx
│ ├── Table.tsx
│ ├── TableCell.tsx
│ ├── TableRow.tsx
│ └── index.ts
│ ├── Tabs
│ ├── Tabs.js
│ ├── Tabs.stories.jsx
│ ├── TabsContent.js
│ ├── TabsList.js
│ ├── TabsTrigger.js
│ └── index.js
│ ├── Text
│ ├── Text.stories.tsx
│ ├── Text.tsx
│ └── index.ts
│ ├── Toolbar
│ ├── Logo.tsx
│ ├── Toolbar.stories.tsx
│ ├── Toolbar.test.tsx
│ ├── Toolbar.tsx
│ ├── ToolbarUtilNav.tsx
│ └── index.tsx
│ ├── Tooltip
│ ├── Tooltip.css
│ ├── Tooltip.stories.tsx
│ ├── Tooltip.tsx
│ └── index.ts
│ ├── Wizard
│ ├── Wizard.stories.tsx
│ ├── Wizard.tsx
│ ├── WizardStep.tsx
│ └── index.ts
│ ├── assets
│ └── a8cLogo.tsx
│ ├── index.js
│ ├── theme
│ ├── breakpoints.ts
│ ├── colors.js
│ ├── generated
│ │ ├── valet-theme-dark.json
│ │ └── valet-theme-light.json
│ ├── getPropValue.js
│ └── index.js
│ └── utils
│ ├── random.js
│ └── stories
│ └── CustomLink.tsx
├── test
├── fileMock.ts
└── setupAfterEnv.ts
├── tokens
├── utilities
│ ├── .DS_Store
│ ├── colors
│ │ ├── color output
│ │ │ ├── blue.json
│ │ │ ├── gold.json
│ │ │ ├── gray.json
│ │ │ ├── green.json
│ │ │ ├── orange.json
│ │ │ ├── pink.json
│ │ │ ├── red.json
│ │ │ ├── salmon.json
│ │ │ └── yellow.json
│ │ ├── colorOutput.json
│ │ ├── color_3d_plot.js
│ │ ├── color_graph.js
│ │ ├── colors.json
│ │ ├── index.js
│ │ ├── package-lock.json
│ │ └── package.json
│ └── figma-type-calculator
│ │ └── responsive-type.js
└── valet-core
│ ├── $metadata.json
│ ├── $themes.json
│ ├── valet-core.json
│ ├── wpvip-product-core.json
│ └── wpvip-product-dark.json
├── tsconfig.definition.json
└── tsconfig.json
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "image": "mcr.microsoft.com/vscode/devcontainers/javascript-node:0-18",
3 | "customizations": {
4 | "vscode": {
5 | "settings": {
6 | "extensions.ignoreRecommendations": true,
7 | "git.autofetch": true,
8 | "editor.defaultFormatter": "esbenp.prettier-vscode",
9 | "[svg]": {
10 | "editor.defaultFormatter": "jock.svg"
11 | },
12 | "prettier.prettierPath": "./node_modules/.bin/prettier",
13 | "editor.formatOnSave": true
14 | },
15 | "extensions": [
16 | "dbaeumer.vscode-eslint",
17 | "deque-systems.vscode-axe-linter",
18 | "eamodio.gitlens",
19 | "editorconfig.editorconfig",
20 | "esbenp.prettier-vscode",
21 | "github.vscode-pull-request-github",
22 | "ionutvmi.path-autocomplete",
23 | "jock.svg",
24 | "styled-components.vscode-styled-components",
25 | "unifiedjs.vscode-mdx",
26 | "usernamehw.errorlens"
27 | ]
28 | }
29 | },
30 | "features": {
31 | "git": "latest",
32 | "github-cli": "latest"
33 | },
34 | "forwardPorts": [ 6006 ],
35 | "onCreateCommand": "npm install",
36 | "portsAttributes": {
37 | "6006": {
38 | "label": "Storybook",
39 | "onAutoForward": "openPreview"
40 | }
41 | },
42 | "postAttachCommand": "npm run dev"
43 | }
44 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_style = tab
7 | insert_final_newline = true
8 | trim_trailing_whitespace = true
9 |
10 | [*.{json,yaml,yml}]
11 | indent_style = space
12 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | .git
2 | build
3 | coverage
4 | flow-typed
5 | jest_0
6 | node_modules
7 | storybook-static
8 | tokens/
9 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | require( '@automattic/eslint-plugin-wpvip/init' );
2 |
3 | module.exports = {
4 | extends: [
5 | 'plugin:@automattic/wpvip/recommended',
6 | 'plugin:@automattic/wpvip/weak-javascript',
7 | 'plugin:@automattic/wpvip/weak-testing',
8 | 'plugin:storybook/recommended',
9 | ],
10 | globals: {
11 | alert: true,
12 | window: true,
13 | },
14 | rules: {
15 | complexity: 'off',
16 | 'id-length': 'off',
17 | 'import/no-extraneous-dependencies': 'off',
18 | 'jsx-a11y/click-events-have-key-events': 'off',
19 | '@typescript-eslint/no-unsafe-call': 'off',
20 | '@typescript-eslint/no-unsafe-member-access': 'off',
21 | '@typescript-eslint/no-unsafe-assignment': 'off',
22 | 'jsx-a11y/no-autofocus': 'off',
23 | 'jsx-a11y/no-static-element-interactions': 'off',
24 | 'no-prototype-builtins': 'off',
25 | 'prettier/prettier': 'error',
26 | 'react/no-unknown-property': 'off',
27 | 'react-hooks/exhaustive-deps': 'off',
28 | 'security/detect-object-injection': 'off',
29 | },
30 | };
31 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Description
2 |
3 | A few sentences describing the overall goals of the pull request. Any special considerations and decisions.
4 |
5 | Also include any references to relevant discussions and documentation.
6 |
7 | ## Checklist
8 |
9 | - [ ] This PR has good automated test coverage
10 | - [ ] The storybook for the component has been updated
11 |
12 | ## Steps to Test
13 |
14 | Outline the steps to test and verify the PR here.
15 |
16 | Example:
17 |
18 | 1. Pull down PR.
19 | 1. `npm run dev`.
20 | 1. Open Storybook.
21 | 1. Eat cookies.
22 | 1. Verify cookies are delicious.
23 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - trunk
8 | workflow_dispatch:
9 |
10 | concurrency:
11 | group: ${{ github.workflow }}-${{ github.ref }}
12 | cancel-in-progress: true
13 |
14 | permissions:
15 | contents: read
16 |
17 | jobs:
18 | lint:
19 | name: Lint and check types
20 | runs-on: ubuntu-latest
21 | steps:
22 | - name: Setup and install
23 | uses: Automattic/vip-actions/nodejs-setup@trunk
24 | with:
25 | node-version-file: .nvmrc
26 |
27 | - name: Run linter
28 | run: npm run lint
29 |
30 | - name: Check formatting
31 | run: npm run format:check
32 |
33 | - name: Check types
34 | run: npm run check-types
35 |
36 | dependaban:
37 | name: Dependaban
38 | runs-on: ubuntu-latest
39 | steps:
40 | - uses: Automattic/vip-actions/dependaban@trunk
41 |
42 | jest:
43 | name: Jest tests
44 | needs: [lint]
45 | runs-on: ubuntu-latest
46 | steps:
47 | - name: Setup and install
48 | uses: Automattic/vip-actions/nodejs-setup@trunk
49 | with:
50 | node-version-file: .nvmrc
51 |
52 | - name: Run Tests
53 | run: npm run jest
54 |
55 | build:
56 | name: Build
57 | needs: [lint]
58 | runs-on: ubuntu-latest
59 | steps:
60 | - name: Setup and install
61 | uses: Automattic/vip-actions/nodejs-setup@trunk
62 | with:
63 | node-version-file: .nvmrc
64 |
65 | - name: Build
66 | run: npm run build
67 |
--------------------------------------------------------------------------------
/.github/workflows/npm-prepare-release.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Prepare new npm release
3 |
4 | on:
5 | workflow_dispatch:
6 | inputs:
7 | npm-version-type:
8 | description: 'The npm version type we are publishing.'
9 | required: true
10 | type: choice
11 | default: 'patch'
12 | options:
13 | - patch
14 | - minor
15 | - major
16 |
17 | jobs:
18 | prepare:
19 | name: Prepare a new npm release
20 | runs-on: ubuntu-latest
21 | steps:
22 | - name: Check out the source code
23 | uses: actions/checkout@v3
24 |
25 | - name: Run npm-prepare-release
26 | uses: Automattic/vip-actions/npm-prepare-release@v0.1.2
27 | with:
28 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
29 | npm-version-type: ${{ inputs.npm-version-type }}
30 |
--------------------------------------------------------------------------------
/.github/workflows/npm-publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish to npm (if applicable)
2 |
3 | on:
4 | pull_request:
5 | types: [closed]
6 |
7 | jobs:
8 | publish:
9 | name: Publish to npm
10 | runs-on: ubuntu-latest
11 | if: contains( github.event.pull_request.labels.*.name, '[ Type ] NPM version update' ) && startsWith( github.head_ref, 'release/') && github.event.pull_request.merged == true
12 | steps:
13 | - uses: Automattic/vip-actions/npm-publish@v0.1.2
14 | with:
15 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
16 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
17 |
--------------------------------------------------------------------------------
/.github/workflows/stale.yml:
--------------------------------------------------------------------------------
1 | name: Stale monitor
2 |
3 | on:
4 | schedule:
5 | - cron: '0 0 * * *'
6 |
7 | jobs:
8 | stale:
9 | name: Stale
10 | runs-on: ubuntu-latest
11 | permissions:
12 | issues: write
13 | pull-requests: write
14 | steps:
15 | - uses: Automattic/vip-actions/stale@trunk
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 | storybook-static
4 | coverage
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 18.16.1
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | build
2 | coverage
3 | node_modules
4 | storybook-static
5 | src/system/theme/generated
6 | tokens/
7 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | "@automattic/eslint-plugin-wpvip/prettierrc"
2 |
--------------------------------------------------------------------------------
/.storybook/decorators/withBoundingBox.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeDecorator } from '@storybook/addons';
3 | import { Box } from '../../src/system';
4 | import { Global, css } from '@emotion/react';
5 |
6 | export default makeDecorator( {
7 | name: 'withBoundingBox',
8 | parameterName: 'colorMode2',
9 | wrapper: storyFn => (
10 | <>
11 |
Add your key team members to the VIP Dashboard.
22 |Add developers to GitHub.
23 |Add content editors and developers to WordPress admin.
24 |
26 | { ' ' }
29 | Code
30 | >
31 | }
32 | />
33 | ),
34 | };
35 |
36 | export const DefaultWithIcon: Story = {
37 | render: () => Code with Icon
,
38 | };
39 |
40 | export const DefaultWithConsoleInfo: Story = {
41 | render: () => (
42 | global.alert( 'Hello world' ) }>
43 | Code with Icon and Click callback — console.info
44 |
45 | ),
46 | };
47 |
--------------------------------------------------------------------------------
/src/system/Code/Code.test.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * External dependencies
3 | */
4 | import { render, screen, fireEvent, waitFor } from '@testing-library/react';
5 | import { axe } from 'jest-axe';
6 |
7 | /**
8 | * Internal dependencies
9 | */
10 | import { Code } from './Code';
11 |
12 | const defaultProps = {
13 | showCopy: false,
14 | };
15 |
16 | // Mock navigator.clipboard because jsdom doesn't support it
17 | Object.defineProperty( window.navigator, 'clipboard', {
18 | value: {
19 | writeText: () => {
20 | return Promise.resolve();
21 | },
22 | },
23 | } );
24 |
25 | describe( '
', () => {
26 | it( 'renders the Code component', async () => {
27 | const { container } = render( This is a code
);
28 |
29 | expect( screen.getByText( 'This is a code' ) ).toBeInTheDocument();
30 |
31 | // Check for accessibility issues
32 | expect( await axe( container ) ).toHaveNoViolations();
33 | } );
34 |
35 | it( 'renders the Code component with a copy button', async () => {
36 | const props = { ...defaultProps, showCopy: true };
37 | const { container } = render( This is a code
);
38 |
39 | expect( screen.getByRole( 'button', { name: 'Copy code' } ) ).toBeInTheDocument();
40 |
41 | // Check for accessibility issues
42 | expect( await axe( container ) ).toHaveNoViolations();
43 | } );
44 |
45 | it( 'updates the the UI after clicking on "Copy"', async () => {
46 | const props = { ...defaultProps, showCopy: true };
47 | const { container } = render( This is a code
);
48 |
49 | fireEvent.click( screen.getByRole( 'button', { name: 'Copy code' } ) );
50 |
51 | await waitFor( () => new Promise( resolve => setTimeout( resolve, 0 ) ) );
52 |
53 | expect( screen.getByText( 'Code copied to clipboard' ) ).toBeInTheDocument();
54 |
55 | // Check for accessibility issues
56 | expect( await axe( container ) ).toHaveNoViolations();
57 | } );
58 | } );
59 |
--------------------------------------------------------------------------------
/src/system/Code/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Internal dependencies
3 | */
4 | export { Code } from './Code';
5 |
--------------------------------------------------------------------------------
/src/system/ConfirmationDialog/ConfirmationDialog.js:
--------------------------------------------------------------------------------
1 | /** @jsxImportSource theme-ui */
2 |
3 | /**
4 | * External dependencies
5 | */
6 | import classNames from 'classnames';
7 | import PropTypes from 'prop-types';
8 | import React from 'react';
9 |
10 | /**
11 | * Internal dependencies
12 | */
13 | import { Dialog, Box, Heading, Text, Flex, Button } from '../';
14 |
15 | const ConfirmationDialogContent = ( {
16 | title,
17 | body,
18 | onClose,
19 | label = 'Confirm',
20 | onConfirm,
21 | className = null,
22 | } ) => (
23 |
24 |
25 | { title }
26 |
27 | { body }
28 |
29 |
32 |
41 |
42 |
43 | );
44 |
45 | ConfirmationDialogContent.propTypes = {
46 | title: PropTypes.node,
47 | body: PropTypes.node,
48 | label: PropTypes.string,
49 | onClose: PropTypes.func,
50 | onConfirm: PropTypes.func,
51 | className: PropTypes.any,
52 | };
53 |
54 | const ConfirmationDialog = ( { trigger, onConfirm, needsConfirm = true, ...props } ) => {
55 | const directTrigger = React.cloneElement( trigger, { onClick: onConfirm } );
56 |
57 | if ( ! needsConfirm ) {
58 | return directTrigger;
59 | }
60 |
61 | return (
62 |