diff --git a/packages/core/upload/admin/src/components/FolderCard/FolderCard.js b/packages/core/upload/admin/src/components/FolderCard/FolderCard.js new file mode 100644 index 0000000000..a297ffa2ba --- /dev/null +++ b/packages/core/upload/admin/src/components/FolderCard/FolderCard.js @@ -0,0 +1,92 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import styled from 'styled-components'; + +import { pxToRem } from '@strapi/helper-plugin'; +import { Box } from '@strapi/design-system/Box'; +import { Stack } from '@strapi/design-system/Stack'; +import Folder from '@strapi/icons/Folder'; + +import { FolderCardContext } from './FolderCardContext'; +import useId from './utils/useId'; + +const FauxClickWrapper = styled.button` + height: 100%; + left: 0; + position: absolute; + opacity: 0; + top: 0; + width: 100%; + + &:hover, + &:focus { + text-decoration: none; + } +`; + +const StyledFolder = styled(Folder)` + path { + fill: currentColor; + } +`; + +export const FolderCard = ({ children, id, startAction, ariaLabel, onDoubleClick, ...props }) => { + const generatedId = useId(id); + + return ( + + + event.preventDefault()} + onDoubleClick={onDoubleClick} + zIndex={1} + tabIndex={-1} + aria-label={ariaLabel} + aria-hidden + /> + + + {startAction} + + + + + + {children} + + + + ); +}; + +FolderCard.defaultProps = { + id: undefined, +}; + +FolderCard.propTypes = { + ariaLabel: PropTypes.string.isRequired, + children: PropTypes.node.isRequired, + id: PropTypes.string, + onDoubleClick: PropTypes.func.isRequired, + startAction: PropTypes.element.isRequired, +}; diff --git a/packages/core/upload/admin/src/components/FolderCard/FolderCardBody.js b/packages/core/upload/admin/src/components/FolderCard/FolderCardBody.js new file mode 100644 index 0000000000..bd7ecb5cb5 --- /dev/null +++ b/packages/core/upload/admin/src/components/FolderCard/FolderCardBody.js @@ -0,0 +1,25 @@ +import React from 'react'; +import styled from 'styled-components'; + +import { Flex } from '@strapi/design-system/Flex'; + +import { useFolderCard } from './FolderCardContext'; + +const StyledBox = styled(Flex)` + user-select: none; +`; + +export const FolderCardBody = props => { + const { id } = useFolderCard(); + + return ( + + ); +}; diff --git a/packages/core/upload/admin/src/components/FolderCard/FolderCardCheckbox.js b/packages/core/upload/admin/src/components/FolderCard/FolderCardCheckbox.js new file mode 100644 index 0000000000..ced28a17cc --- /dev/null +++ b/packages/core/upload/admin/src/components/FolderCard/FolderCardCheckbox.js @@ -0,0 +1,14 @@ +import React from 'react'; +import { Box } from '@strapi/design-system/Box'; +import { BaseCheckbox } from '@strapi/design-system/BaseCheckbox'; +import { useFolderCard } from './FolderCardContext'; + +export const FolderCardCheckbox = props => { + const { id } = useFolderCard(); + + return ( + + + + ); +}; diff --git a/packages/core/upload/admin/src/components/FolderCard/FolderCardContext.js b/packages/core/upload/admin/src/components/FolderCard/FolderCardContext.js new file mode 100644 index 0000000000..985ae836d9 --- /dev/null +++ b/packages/core/upload/admin/src/components/FolderCard/FolderCardContext.js @@ -0,0 +1,7 @@ +import { createContext, useContext } from 'react'; + +export const FolderCardContext = createContext(); + +export function useFolderCard() { + return useContext(FolderCardContext); +} diff --git a/packages/core/upload/admin/src/components/FolderCard/FolderCardLink.js b/packages/core/upload/admin/src/components/FolderCard/FolderCardLink.js new file mode 100644 index 0000000000..f5cf80ceb5 --- /dev/null +++ b/packages/core/upload/admin/src/components/FolderCard/FolderCardLink.js @@ -0,0 +1,7 @@ +import styled from 'styled-components'; + +import { BaseLink } from '@strapi/design-system/BaseLink'; + +export const FolderCardLink = styled(BaseLink)` + text-decoration: none; +`; diff --git a/packages/core/upload/admin/src/components/FolderCard/index.js b/packages/core/upload/admin/src/components/FolderCard/index.js new file mode 100644 index 0000000000..4da01d2615 --- /dev/null +++ b/packages/core/upload/admin/src/components/FolderCard/index.js @@ -0,0 +1,4 @@ +export { FolderCard } from './FolderCard'; +export { FolderCardBody } from './FolderCardBody'; +export { FolderCardCheckbox } from './FolderCardCheckbox'; +export { FolderCardLink } from './FolderCardLink'; diff --git a/packages/core/upload/admin/src/components/FolderCard/tests/FolderCard.test.js b/packages/core/upload/admin/src/components/FolderCard/tests/FolderCard.test.js new file mode 100644 index 0000000000..e91e4408d5 --- /dev/null +++ b/packages/core/upload/admin/src/components/FolderCard/tests/FolderCard.test.js @@ -0,0 +1,65 @@ +import React from 'react'; +import { BaseLink } from '@strapi/design-system/BaseLink'; +import { Flex } from '@strapi/design-system/Flex'; +import { ThemeProvider, lightTheme } from '@strapi/design-system'; +import { Typography } from '@strapi/design-system/Typography'; +import { render, fireEvent } from '@testing-library/react'; + +import { FolderCard } from '../FolderCard'; +import { FolderCardBody } from '../FolderCardBody'; +import { FolderCardCheckbox } from '../FolderCardCheckbox'; + +const ID_FIXTURE = 'folder-1'; + +// eslint-disable-next-line react/prop-types +const ComponentFixture = ({ children, ...props }) => { + return ( + + } + onDoubleClick={() => {}} + {...props} + > + {children || ''} + + + ); +}; + +describe('FolderCard', () => { + it('renders and matches the snapshot', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it('properly calls the onDoubleClick callback', () => { + const callback = jest.fn(); + const { container } = render(); + + fireEvent(container.querySelector('a'), new MouseEvent('dblclick', { bubbles: true })); + + expect(callback).toHaveBeenCalledTimes(1); + }); + + it('has all required ids set when rendering a start action', () => { + const { container } = render( + }> + + + + + Pictures + + + + + + ); + + expect(container.querySelector(`[id="${ID_FIXTURE}-title"]`)).toBeInTheDocument(); + expect(container.querySelector(`[aria-labelledby="${ID_FIXTURE}-title"]`)).toBeInTheDocument(); + }); +}); diff --git a/packages/core/upload/admin/src/components/FolderCard/tests/__snapshots__/FolderCard.test.js.snap b/packages/core/upload/admin/src/components/FolderCard/tests/__snapshots__/FolderCard.test.js.snap new file mode 100644 index 0000000000..2851e7ab2a --- /dev/null +++ b/packages/core/upload/admin/src/components/FolderCard/tests/__snapshots__/FolderCard.test.js.snap @@ -0,0 +1,149 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FolderCard renders and matches the snapshot 1`] = ` +.c1 { + cursor: pointer; +} + +.c8 { + border: 0; + -webkit-clip: rect(0 0 0 0); + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + +.c0 { + position: relative; +} + +.c6 { + background: #eaf5ff; + color: #66b7f1; + padding-top: 8px; + padding-right: 12px; + padding-bottom: 8px; + padding-left: 12px; + border-radius: 4px; +} + +.c3 { + background: #ffffff; + padding-top: 12px; + padding-right: 16px; + padding-bottom: 12px; + padding-left: 16px; + border-radius: 4px; + box-shadow: 0px 1px 4px rgba(33,33,52,0.1); + cursor: pointer; + cursor: pointer; +} + +.c4 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c5 > * { + margin-left: 0; + margin-right: 0; +} + +.c5 > * + * { + margin-left: 12px; +} + +.c2 { + height: 100%; + left: 0; + position: absolute; + opacity: 0; + top: 0; + width: 100%; +} + +.c2:hover, +.c2:focus { + -webkit-text-decoration: none; + text-decoration: none; +} + +.c7 path { + fill: currentColor; +} + +
+