diff --git a/packages/clay-panel/package.json b/packages/clay-panel/package.json index 373962dc0c..4271402eee 100644 --- a/packages/clay-panel/package.json +++ b/packages/clay-panel/package.json @@ -11,20 +11,29 @@ "main": "lib/index.js", "module": "src/index.tsx", "jsnext:main": "src/index.tsx", - "files": ["lib", "src"], + "files": [ + "lib", + "src" + ], "scripts": { "build": "cross-env NODE_ENV=production babel src --root-mode upward --out-dir lib --extensions .ts,.tsx", "build:types": "cross-env NODE_ENV=production tsc --project ./tsconfig.declarations.json", "prepublishOnly": "npm run build && npm run build:types", "test": "jest --config ../../jest.config.js" }, - "keywords": ["clay", "react"], + "keywords": [ + "clay", + "react" + ], "dependencies": { + "@clayui/icon": "^3.0.0", "classnames": "^2.2.6" }, "peerDependencies": { "react": "^16.8.1", "react-dom": "^16.8.1" }, - "browserslist": ["extends browserslist-config-clay"] + "browserslist": [ + "extends browserslist-config-clay" + ] } diff --git a/packages/clay-panel/src/Body.tsx b/packages/clay-panel/src/Body.tsx new file mode 100644 index 0000000000..73e24cf571 --- /dev/null +++ b/packages/clay-panel/src/Body.tsx @@ -0,0 +1,24 @@ +/** + * © 2019 Liferay, Inc. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +import * as React from 'react'; +import classNames from 'classnames'; + +interface Props extends React.HTMLAttributes {} + +const ClayPanelBody: React.FunctionComponent = ({ + children, + className, + ...otherProps +}) => { + return ( +
+ {children} +
+ ); +}; + +export default ClayPanelBody; diff --git a/packages/clay-panel/src/Footer.tsx b/packages/clay-panel/src/Footer.tsx new file mode 100644 index 0000000000..cb2b59b4da --- /dev/null +++ b/packages/clay-panel/src/Footer.tsx @@ -0,0 +1,24 @@ +/** + * © 2019 Liferay, Inc. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +import * as React from 'react'; +import classNames from 'classnames'; + +interface Props extends React.HTMLAttributes {} + +const ClayPanelFooter: React.FunctionComponent = ({ + children, + className, + ...otherProps +}) => { + return ( +
+ {children} +
+ ); +}; + +export default ClayPanelFooter; diff --git a/packages/clay-panel/src/Group.tsx b/packages/clay-panel/src/Group.tsx new file mode 100644 index 0000000000..67d29e6a86 --- /dev/null +++ b/packages/clay-panel/src/Group.tsx @@ -0,0 +1,62 @@ +/** + * © 2019 Liferay, Inc. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +import * as React from 'react'; +import classNames from 'classnames'; + +interface Props extends React.HTMLAttributes { + /** + * Flag to signify that `panel-group-fluid-first` class should be added. + * This class generally should be used inside card or sheet + */ + fluidFirst?: boolean; + + /** + * Flag to signify that `panel-group-fluid-last` class should be added. + * This class generally should be used inside card or sheet + */ + fluidLast?: boolean; + + /** + * Flag to signify that `panel-group-fluid` class should be added. + * This class generally should be used inside card or sheet + */ + fluid?: boolean; + + /** + * Flag to signify that `panel-group-flush` class should be added. + * This class generally should be used inside card, sheet, or a type of padded container. + */ + flush?: boolean; +} + +const ClayPanelGroup: React.FunctionComponent = ({ + children, + className, + fluid, + fluidFirst, + fluidLast, + flush, + ...otherProps +}) => { + return ( +
+ {children} +
+ ); +}; + +export default ClayPanelGroup; diff --git a/packages/clay-panel/src/Header.tsx b/packages/clay-panel/src/Header.tsx new file mode 100644 index 0000000000..9c62298243 --- /dev/null +++ b/packages/clay-panel/src/Header.tsx @@ -0,0 +1,24 @@ +/** + * © 2019 Liferay, Inc. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +import * as React from 'react'; +import classNames from 'classnames'; + +interface Props extends React.HTMLAttributes {} + +const ClayPanelHeader: React.FunctionComponent = ({ + children, + className, + ...otherProps +}) => { + return ( +
+ {children} +
+ ); +}; + +export default ClayPanelHeader; diff --git a/packages/clay-panel/src/__tests__/__snapshots__/index.tsx.snap b/packages/clay-panel/src/__tests__/__snapshots__/index.tsx.snap index d1e5c85a7a..c76926c95b 100644 --- a/packages/clay-panel/src/__tests__/__snapshots__/index.tsx.snap +++ b/packages/clay-panel/src/__tests__/__snapshots__/index.tsx.snap @@ -1 +1,158 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ClayPanel renders 1`] = ` +
+
+ + Display Title + +
+
+ Header! +
+
+ Body! +
+
+ Footer! +
+
+`; + +exports[`ClayPanel renders with different displayType 1`] = ` +
+
+ + Display Title + +
+
+ Header! +
+
+ Body! +
+
+ Footer! +
+
+`; + +exports[`ClayPanel renders with multiple panels 1`] = ` +
+
+
+ + Display Title + +
+
+ Body! +
+
+
+
+ + Display Title + +
+
+ Body! +
+
+
+ +
+
+ Body! +
+
+
+
+`; diff --git a/packages/clay-panel/src/__tests__/index.tsx b/packages/clay-panel/src/__tests__/index.tsx index adad92d26d..751846c2b2 100644 --- a/packages/clay-panel/src/__tests__/index.tsx +++ b/packages/clay-panel/src/__tests__/index.tsx @@ -7,13 +7,95 @@ import * as React from 'react'; import * as TestRenderer from 'react-test-renderer'; import ClayPanel from '..'; +import {cleanup, fireEvent, render} from 'react-testing-library'; describe('ClayPanel', () => { it('renders', () => { const testRenderer = TestRenderer.create( - + + {'Header!'} + {'Body!'} + {'Footer!'} + ); expect(testRenderer.toJSON()).toMatchSnapshot(); }); + + it('renders with different displayType', () => { + const testRenderer = TestRenderer.create( + + {'Header!'} + {'Body!'} + {'Footer!'} + + ); + + expect(testRenderer.toJSON()).toMatchSnapshot(); + }); + + it('renders with multiple panels', () => { + const testRenderer = TestRenderer.create( + + + {'Body!'} + + + + {'Body!'} + + + + {'Body!'} + + + ); + + expect(testRenderer.toJSON()).toMatchSnapshot(); + }); +}); + +describe('ClayPanel Interactions', () => { + afterEach(cleanup); + + it('clicking the title should expand and close the content', () => { + const {container} = render( + + {'Header!'} + {'Body!'} + {'Footer!'} + + ); + + const closeButton = container.querySelector('.panel-header'); + + expect(container.querySelector('.panel-collapse.show')).toBeFalsy(); + expect( + container.querySelector('.panel-collapse.collapse') + ).toBeTruthy(); + + fireEvent.click(closeButton as HTMLButtonElement, {}); + + expect(container.querySelector('.panel-collapse.show')).toBeTruthy(); + expect(container.querySelector('.panel-collapse.collapse')).toBeFalsy(); + + fireEvent.click(closeButton as HTMLButtonElement, {}); + + expect(container.querySelector('.panel-collapse.show')).toBeFalsy(); + expect( + container.querySelector('.panel-collapse.collapse') + ).toBeTruthy(); + }); }); diff --git a/packages/clay-panel/src/index.tsx b/packages/clay-panel/src/index.tsx index bab7a77212..8a705e9f8e 100644 --- a/packages/clay-panel/src/index.tsx +++ b/packages/clay-panel/src/index.tsx @@ -6,16 +6,112 @@ import * as React from 'react'; import classNames from 'classnames'; +import ClayIcon from '@clayui/icon'; +import ClayPanelBody from './Body'; +import ClayPanelFooter from './Footer'; +import ClayPanelGroup from './Group'; +import ClayPanelHeader from './Header'; -interface Props extends React.HTMLAttributes {} +interface Props extends React.HTMLAttributes { + collapsable?: boolean; + collapseClassNames?: string; + defaultExpanded?: boolean; + displayTitle?: React.ReactText; + displayType?: 'unstyled' | 'secondary'; + showCollapseIcon?: boolean; + spritemap?: string; +} -const ClayPanel: React.FunctionComponent = ({ +const ClayPanel: React.FunctionComponent & { + Body: typeof ClayPanelBody; + Footer: typeof ClayPanelFooter; + Group: typeof ClayPanelGroup; + Header: typeof ClayPanelHeader; +} = ({ + children, className, + collapsable, + collapseClassNames, + defaultExpanded = false, + displayTitle, + displayType, + showCollapseIcon = true, + spritemap, ...otherProps }) => { + const [expanded, setExpaned] = React.useState(defaultExpanded); + return ( -
{'ClayPanel'}
+
+ {!collapsable && ( + <> + {displayTitle && ( + + {displayTitle} + + )} + + {children} + + )} + + {collapsable && ( + <> + +
+ {children} +
+ + )} +
); }; +ClayPanel.Body = ClayPanelBody; +ClayPanel.Group = ClayPanelGroup; +ClayPanel.Footer = ClayPanelFooter; +ClayPanel.Header = ClayPanelHeader; + export default ClayPanel; diff --git a/packages/clay-panel/stories/index.tsx b/packages/clay-panel/stories/index.tsx index 34cb1ea5ab..846a62422f 100644 --- a/packages/clay-panel/stories/index.tsx +++ b/packages/clay-panel/stories/index.tsx @@ -3,14 +3,90 @@ * * SPDX-License-Identifier: BSD-3-Clause */ + +import ClayPanel from '../src'; import React from 'react'; import {boolean, select, text} from '@storybook/addon-knobs'; import {storiesOf} from '@storybook/react'; -import ClayPanel from '../src'; import 'clay-css/lib/css/atlas.css'; +const spritemap = require('clay-css/lib/images/icons/icons.svg'); + storiesOf('ClayPanel', module) .add('default', () => ( - - )); \ No newline at end of file + + {'Header!'} + {'Body!'} + {'Footer!'} + + )) + .add('collapsable', () => ( + + {'Here is some content inside.'} + + )) + .add('groups', () => ( + + {['One', 'Two', 'Three'].map(item => ( + + + {`Here is some content inside for number ${item}`} + + + ))} + + )) + .add('w/ sheet', () => ( +
+ + {['One', 'Two', 'Three'].map(item => ( + + + {`Here is some content inside for number ${item}`} + + + ))} + +
+ ));