diff --git a/.github/ISSUE_TEMPLATE/demo-improvements.md b/.github/ISSUE_TEMPLATE/demo-improvements.md deleted file mode 100644 index f97700471e..0000000000 --- a/.github/ISSUE_TEMPLATE/demo-improvements.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Demo Improvements -about: Demo is not following the agreements -title: Component DEMO -labels: '' -assignees: '' - ---- - -# Acceptance Criteria -The `COMPONENT` demo viewed on sui-studio is not following the agreements. -It can be previewed on the following link: - - -# Definition Of DONE - - - -> ### Jira Task - diff --git a/.github/ISSUE_TEMPLATE/demo-improvements.yml b/.github/ISSUE_TEMPLATE/demo-improvements.yml new file mode 100644 index 0000000000..683fb6e263 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/demo-improvements.yml @@ -0,0 +1,28 @@ +name: Demo Improvements +description: Demo is not following the agreements +title: '🎨 ' +labels: [demo] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this new component definition! + - type: textarea + attributes: + label: Acceptance Criteria + description: Please add a clear and concise predefined requirements that must be met to mark a user story complete. + validations: + required: false + - type: textarea + attributes: + label: Definition of done (DOD) + description: An agreed-upon set of items that must be completed before a project or user story can be considered complete + validations: + required: false + - type: textarea + attributes: + label: Additional info + description: Add any other context about the problem here. + validations: + required: false + diff --git a/.github/ISSUE_TEMPLATE/propose-a-new-component.md b/.github/ISSUE_TEMPLATE/propose-a-new-component.md deleted file mode 100644 index 9c2b9d76b8..0000000000 --- a/.github/ISSUE_TEMPLATE/propose-a-new-component.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: Propose a new component -about: Do you need a new component? -title: '' -labels: '' -assignees: '' - ---- - -**Is your component proposal related to a problem? Please describe.** -A clear and concise description of what the problem is. - -**Describe the solution you'd like** -A clear and concise description of what you want to have. - -**Describe alternatives you've considered** -Have you considered iterating an existing component? -If so, please add a clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/propose-a-new-component.yml b/.github/ISSUE_TEMPLATE/propose-a-new-component.yml new file mode 100644 index 0000000000..1be90126ef --- /dev/null +++ b/.github/ISSUE_TEMPLATE/propose-a-new-component.yml @@ -0,0 +1,40 @@ +name: Propose a new component +description: Do you need a new component? +title: '🧱 ' +labels: [] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this new component definition! + - type: textarea + attributes: + label: Is your component proposal related to a problem? Please describe. + description: Please add a clear and concise description of what the current problem is. + validations: + required: false + - type: textarea + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to have. + validations: + required: false + - type: textarea + attributes: + label: Describe alternatives you've considered + description: | + Have you considered iterating an existing component? + If so, please add a clear and concise description of any alternative solutions or features you've considered. + validations: + required: false + - type: textarea + attributes: + label: Additional context** + description: Add any other context or screenshots about the feature request here. + - type: textarea + attributes: + label: Additional info + description: Add any other context about the problem here. + validations: + required: false + diff --git a/.github/ISSUE_TEMPLATE/propose-an-improvement.yml b/.github/ISSUE_TEMPLATE/propose-an-improvement.yml index e25cce2734..60151d748f 100644 --- a/.github/ISSUE_TEMPLATE/propose-an-improvement.yml +++ b/.github/ISSUE_TEMPLATE/propose-an-improvement.yml @@ -1,7 +1,7 @@ name: Improve an existing component description: Suggest to add a new Prop, SCSS Variable and else title: '✨' -labels: [] +labels: [enhancement] body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/report-a-bug.yml b/.github/ISSUE_TEMPLATE/report-a-bug.yml index 86a23484a4..b98ae32005 100644 --- a/.github/ISSUE_TEMPLATE/report-a-bug.yml +++ b/.github/ISSUE_TEMPLATE/report-a-bug.yml @@ -1,7 +1,7 @@ name: Report a bug / issue description: Report a bug / issue to help us improve SUI title: '🪲 ' -labels: [] +labels: [bug] body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/test.yml b/.github/ISSUE_TEMPLATE/test.yml new file mode 100644 index 0000000000..e4799c04d1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/test.yml @@ -0,0 +1,34 @@ +name: Test +description: Report test case +title: '🧪 ' +labels: [test] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this test addition report! + + Testing purports commitment against users. + - type: input + id: component + attributes: + label: Component + description: Component name + placeholder: component + validations: + required: true + - type: input + id: category + attributes: + label: Category + description: Category name + placeholder: category + validations: + required: true + - type: textarea + attributes: + label: Tests + description: tests needed. + validations: + required: false + diff --git a/.github/workflows/node.js.md b/.github/workflows/node.js.md index f101082c1e..e9553c5844 100644 --- a/.github/workflows/node.js.md +++ b/.github/workflows/node.js.md @@ -24,9 +24,12 @@ stateDiagram-v2 check_skip: check-skip jobs_fork_state_start --> assign_pr_owner jobs_fork_state_start --> build + jobs_fork_state_start --> lint jobs_fork_state_start --> testing build --> deploy + lint --> release + lint --> deploy testing --> release testing --> deploy diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 92ab3cf9e0..6b4e4b437d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,6 +19,38 @@ Some base commands (based on sui-studio) you should acknowledge : * `npm run co` to commit your changes with a wizard. * `npm run release` publish all your changes for all packages (once your PR is merge). +## Commit Conventions +Any commit message given has to follow the pattern: +`[TYPE](components/[CATEGORY]/[COMPONENT]): [MESSAGE]` + +valid `[TYPE]`: + +| [TYPE] | description | action | +|----------|--------------------------------------------------------------------------------------------------------------------------|----------------------------------| +| feat | New features on existing components or even new components also. | CI updates the published version | +| fix | Any kind of update which fixes and error or a bug. | CI updates the published version | +| docs | for documentation only changes | | +| refactor | for necesary refactors that never change the behavior and does not affect to the published component results | | +| perf | for necesary performance refactors that never change the behavior and does not affect to the published component results | | +| test | for adding tests | | +| chore | for project structure changes | | +| release | do not use | | + +Ex: + +`feat(component/atom/button): this is a commit message example of a feature in Atom Button Component` + +`fix(component/molecule/inputTags): this is a commit message example of a bug fix in Molecule InputTags Component` + +When merging a PR with that message, the CI will publish a new minor version of AtomButton/InputTags Component in npm. + +`docs(component/atom/image): this is a commit message example of a documentation improvement on Atom Image` + +When merging a PR with that message, the CI will **NOT** publish a new minor version of AtomImage Component in npm. + +## NPM Conventions +We only use major and minor incremental versioning, and never use the patch. + ## Naming Conventions ### Event Handlers diff --git a/README.md b/README.md index 24f793c222..1801d11af0 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,10 @@ SUI Components is an Open-Source, high quality library of React components that ## 🧪 Test Coverage -![statements](https://shields.io/badge/statements-72.08%25-orange) -![branches](https://shields.io/badge/branches-57.69%25-AA0000) -![functions](https://shields.io/badge/functions-59%25-AA0000) -![lines](https://shields.io/badge/lines-73.74%25-orange) +![statements](https://shields.io/badge/statements-72.1%25-orange) +![branches](https://shields.io/badge/branches-57.78%25-AA0000) +![functions](https://shields.io/badge/functions-59.56%25-AA0000) +![lines](https://shields.io/badge/lines-73.88%25-orange) ## ✨ Features diff --git a/components/README.md b/components/README.md index 8ceaa4816a..936530631b 100644 --- a/components/README.md +++ b/components/README.md @@ -2,21 +2,14 @@ ### SUI Components is an Open-Source, high quality library of React components that empowers teams to craft any product with ease. -## Contribute - -Report a bug or defect +Report a bug Improve an existing component Propose a new component -## Current status - -Performance dashboard - -## Documentation +## Performance Metrics -Contributor +Design System Dashboard -## Coverage -Contributor \ No newline at end of file +Test coverage \ No newline at end of file diff --git a/components/atom/actionButton/CHANGELOG.md b/components/atom/actionButton/CHANGELOG.md index 2524392573..a35eba537e 100644 --- a/components/atom/actionButton/CHANGELOG.md +++ b/components/atom/actionButton/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# 1.10.0 (2022-10-06) + + + # 1.9.0 (2022-06-20) diff --git a/components/atom/actionButton/package.json b/components/atom/actionButton/package.json index 7a1c362e9c..4b7019a90e 100644 --- a/components/atom/actionButton/package.json +++ b/components/atom/actionButton/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-action-button", - "version": "1.9.0", + "version": "1.10.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/atom/backToTop/CHANGELOG.md b/components/atom/backToTop/CHANGELOG.md index 71a4904013..e80af331c4 100644 --- a/components/atom/backToTop/CHANGELOG.md +++ b/components/atom/backToTop/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# 2.3.0 (2022-10-06) + + + # 2.2.0 (2022-06-20) diff --git a/components/atom/backToTop/package.json b/components/atom/backToTop/package.json index 3f26cd02b1..d6538d23d4 100644 --- a/components/atom/backToTop/package.json +++ b/components/atom/backToTop/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-back-to-top", - "version": "2.2.0", + "version": "2.3.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/atom/badge/CHANGELOG.md b/components/atom/badge/CHANGELOG.md index 86a1d202c5..ba43f06040 100644 --- a/components/atom/badge/CHANGELOG.md +++ b/components/atom/badge/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# 1.28.0 (2022-10-06) + + + # 1.27.0 (2022-06-20) diff --git a/components/atom/badge/package.json b/components/atom/badge/package.json index a1db4f93a6..13b97c42ce 100644 --- a/components/atom/badge/package.json +++ b/components/atom/badge/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-badge", - "version": "1.27.0", + "version": "1.28.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/atom/card/CHANGELOG.md b/components/atom/card/CHANGELOG.md index 0a4ee3fbd3..4a7de93d2a 100644 --- a/components/atom/card/CHANGELOG.md +++ b/components/atom/card/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# 1.24.0 (2022-10-06) + + + # 1.23.0 (2022-06-20) diff --git a/components/atom/card/package.json b/components/atom/card/package.json index b52cdb35ed..f24da9b09a 100644 --- a/components/atom/card/package.json +++ b/components/atom/card/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-card", - "version": "1.23.0", + "version": "1.24.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/atom/helpText/CHANGELOG.md b/components/atom/helpText/CHANGELOG.md index a09e43736c..ebf0b88b31 100644 --- a/components/atom/helpText/CHANGELOG.md +++ b/components/atom/helpText/CHANGELOG.md @@ -1,5 +1,26 @@ # CHANGELOG +# 1.9.0 (2022-10-06) + + +### Features + +* **components/atom/helpText:** admit react nodes as a text ([f41967e](https://github.com/SUI-Components/sui-components/commit/f41967ec354662dcc551562ccfe75aa729aff29e)) + + + +# 1.8.0 (2022-09-20) + + +### Features + +* **components/atom/helpText:** add margin top variable ([1235303](https://github.com/SUI-Components/sui-components/commit/123530333dcebbbbad5079de2150e6cd4182dc67)) +* **components/atom/helpText:** update margin variable ([ffd3505](https://github.com/SUI-Components/sui-components/commit/ffd35051631d0561780bdc1af30fa7283fcd53e6)) +* **Root:** Delete undefined dependencies ([c145905](https://github.com/SUI-Components/sui-components/commit/c145905350328925ba6fda2a462d7f8b508c8ea0)) +* **Root:** Merge commit ([d3735d0](https://github.com/SUI-Components/sui-components/commit/d3735d0644332e674d5a5b6291680697f0d6f7c4)) + + + # 1.7.0 (2022-06-20) diff --git a/components/atom/helpText/demo/InputDemo.js b/components/atom/helpText/demo/InputDemo.js deleted file mode 100644 index 921d3e1e4a..0000000000 --- a/components/atom/helpText/demo/InputDemo.js +++ /dev/null @@ -1,17 +0,0 @@ -import {Article, Code, H2, Paragraph} from '@s-ui/documentation-library' -import AtomInput from '@s-ui/react-atom-input' - -import AtomHelpText from '../src/index.js' - -const InputDemo = () => ( -
-

Text

- - The prop text is used to set the text included in. - - - -
-) - -export default InputDemo diff --git a/components/atom/helpText/demo/CheckboxDemo.js b/components/atom/helpText/demo/articles/ArticleCheckbox.js similarity index 68% rename from components/atom/helpText/demo/CheckboxDemo.js rename to components/atom/helpText/demo/articles/ArticleCheckbox.js index 778cac64bc..0888169bda 100644 --- a/components/atom/helpText/demo/CheckboxDemo.js +++ b/components/atom/helpText/demo/articles/ArticleCheckbox.js @@ -1,16 +1,18 @@ import {useState} from 'react' +import PropTypes from 'prop-types' + import {Article, H2} from '@s-ui/documentation-library' import AtomCheckbox from '@s-ui/react-atom-checkbox' import AtomLabel from '@s-ui/react-atom-label' -import AtomHelpText from '../src/index.js' +import AtomHelpText from '../../src/index.js' -const CheckboxDemo = () => { +const ArticleCheckbox = ({className}) => { const [isChecked, setIsChecked] = useState(false) return ( -
+

Checkbox

{ ) } -export default CheckboxDemo +ArticleCheckbox.propTypes = { + className: PropTypes.string +} + +export default ArticleCheckbox diff --git a/components/atom/helpText/demo/articles/ArticleInput.js b/components/atom/helpText/demo/articles/ArticleInput.js new file mode 100644 index 0000000000..d384ec12fd --- /dev/null +++ b/components/atom/helpText/demo/articles/ArticleInput.js @@ -0,0 +1,27 @@ +import PropTypes from 'prop-types' + +import {Article, Code, H2, Paragraph} from '@s-ui/documentation-library' +import AtomInput from '@s-ui/react-atom-input' + +import AtomHelpText from '../../src/index.js' +import {nodeText} from '../settings.js' + +const ArticleInput = ({className}) => ( +
+

Text

+ + The prop text is used to set the text included in. + + + + The component also admits a react node as a text + + +
+) + +ArticleInput.propTypes = { + className: PropTypes.string +} + +export default ArticleInput diff --git a/components/atom/helpText/demo/TextareaDemo.js b/components/atom/helpText/demo/articles/ArticleTextArea.js similarity index 53% rename from components/atom/helpText/demo/TextareaDemo.js rename to components/atom/helpText/demo/articles/ArticleTextArea.js index d491bcfbed..9e3d262e95 100644 --- a/components/atom/helpText/demo/TextareaDemo.js +++ b/components/atom/helpText/demo/articles/ArticleTextArea.js @@ -1,14 +1,20 @@ +import PropTypes from 'prop-types' + import {Article, H2} from '@s-ui/documentation-library' import AtomTextarea from '@s-ui/react-atom-textarea' -import AtomHelpText from '../src/index.js' +import AtomHelpText from '../../src/index.js' -const TextareaDemo = () => ( -
+const ArticleTextArea = ({className}) => ( +

Text-area

) -export default TextareaDemo +ArticleTextArea.propTypes = { + className: PropTypes.string +} + +export default ArticleTextArea diff --git a/components/atom/helpText/demo/index.js b/components/atom/helpText/demo/index.js index 92a0cd43fd..c4ce176913 100644 --- a/components/atom/helpText/demo/index.js +++ b/components/atom/helpText/demo/index.js @@ -2,9 +2,10 @@ import {H1, Paragraph} from '@s-ui/documentation-library' -import CheckboxDemo from './CheckboxDemo.js' -import InputDemo from './InputDemo.js' -import TextareaDemo from './TextareaDemo.js' +import ArticleCheckbox from './articles/ArticleCheckbox.js' +import ArticleInput from './articles/ArticleInput.js' +import ArticleTextArea from './articles/ArticleTextArea.js' +import {CLASS_SECTION} from './settings.js' import './index.scss' @@ -16,11 +17,11 @@ const Demo = () => { Help Text is a feedback that the system gives users to make them clearly understand which information is required - +
- +
- + ) } diff --git a/components/atom/helpText/demo/package.json b/components/atom/helpText/demo/package.json index b876d031b2..9c30b8a8d3 100644 --- a/components/atom/helpText/demo/package.json +++ b/components/atom/helpText/demo/package.json @@ -12,6 +12,7 @@ "license": "ISC", "dependencies": { "@s-ui/react-atom-checkbox": "2", + "@s-ui/react-atom-icon": "1", "@s-ui/react-atom-input": "5", "@s-ui/react-atom-label": "1", "@s-ui/react-atom-textarea": "2" diff --git a/components/atom/helpText/demo/settings.js b/components/atom/helpText/demo/settings.js new file mode 100644 index 0000000000..6c37233a1c --- /dev/null +++ b/components/atom/helpText/demo/settings.js @@ -0,0 +1,17 @@ +import {AntDesignIcon} from '@s-ui/documentation-library' +import AtomIcon from '@s-ui/react-atom-icon' + +export const BASE_CLASS_DEMO = `DemoHelpText` +export const CLASS_SECTION = `${BASE_CLASS_DEMO}-section` + +export const checkIcon = ( + + + +) + +export const nodeText = ( + + node text{checkIcon} + +) diff --git a/components/atom/helpText/package.json b/components/atom/helpText/package.json index a8f5098e81..2185b3da0a 100644 --- a/components/atom/helpText/package.json +++ b/components/atom/helpText/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-help-text", - "version": "1.7.0", + "version": "1.9.0", "description": "", "main": "lib/index.js", "scripts": { @@ -9,7 +9,8 @@ "build:styles": "cpx './src/**/*.scss' ./lib" }, "dependencies": { - "@s-ui/component-dependencies": "1" + "@s-ui/component-dependencies": "1", + "@s-ui/react-primitive-injector": "1" }, "keywords": [], "author": "", diff --git a/components/atom/helpText/src/index.js b/components/atom/helpText/src/index.js index 3e8602f471..63c84fb15f 100644 --- a/components/atom/helpText/src/index.js +++ b/components/atom/helpText/src/index.js @@ -1,13 +1,28 @@ +import {forwardRef} from 'react' + import PropTypes from 'prop-types' +import Injector from '@s-ui/react-primitive-injector' + import {BASE_CLASS} from './settings.js' -const AtomHelpText = ({text}) => {text} +const AtomHelpText = forwardRef(({text}, forwardedRef) => { + const isTextString = typeof text === 'string' + const Component = isTextString ? 'span' : Injector + return ( + + {text} + + ) +}) AtomHelpText.displayName = 'AtomHelpText' AtomHelpText.propTypes = { - text: PropTypes.string.isRequired + text: PropTypes.oneOfType([PropTypes.element, PropTypes.node]).isRequired } export default AtomHelpText diff --git a/components/atom/helpText/src/styles/index.scss b/components/atom/helpText/src/styles/index.scss index e664cea7ad..2d0ed0f136 100644 --- a/components/atom/helpText/src/styles/index.scss +++ b/components/atom/helpText/src/styles/index.scss @@ -4,5 +4,5 @@ $base-class: '.sui-AtomHelpText'; color: $c-help-text; display: block; font-size: $fz-atom-help-text; - margin: $m-s 0 0; + margin: $m-atom-help-text; } diff --git a/components/atom/helpText/src/styles/settings.scss b/components/atom/helpText/src/styles/settings.scss index 5d66a699c3..6947040fbf 100644 --- a/components/atom/helpText/src/styles/settings.scss +++ b/components/atom/helpText/src/styles/settings.scss @@ -1,2 +1,3 @@ $c-help-text: $c-gray-light-3 !default; $fz-atom-help-text: $fz-xs !default; +$m-atom-help-text: $m-s 0 0 !default; diff --git a/components/atom/image/CHANGELOG.md b/components/atom/image/CHANGELOG.md index e2f7a1d541..8519440acd 100644 --- a/components/atom/image/CHANGELOG.md +++ b/components/atom/image/CHANGELOG.md @@ -1,5 +1,32 @@ # CHANGELOG +# 2.16.0 (2022-10-03) + + +### Bug Fixes + +* **components/atom/image:** update proptype ([4b77543](https://github.com/SUI-Components/sui-components/commit/4b77543d4c8537d76c136b1b4a234dc31c2183eb)) + + + +# 2.15.0 (2022-10-01) + + +### Features + +* **components/atom/image:** replace cloneElement spinner ([5871943](https://github.com/SUI-Components/sui-components/commit/58719439ec5e98e8a81d2b77b53e9a7cdd9bbee1)), closes [#2320](https://github.com/SUI-Components/sui-components/issues/2320) + + + +# 2.14.0 (2022-09-29) + + +### Features + +* **components/atom/image:** rename node prop ([4e13e98](https://github.com/SUI-Components/sui-components/commit/4e13e98affe157059edaeaf1843880e017fbc1f1)), closes [#2319](https://github.com/SUI-Components/sui-components/issues/2319) + + + # 2.13.0 (2022-07-18) diff --git a/components/atom/image/README.md b/components/atom/image/README.md index 2ca431e149..6a3200fb7b 100644 --- a/components/atom/image/README.md +++ b/components/atom/image/README.md @@ -26,12 +26,23 @@ import AtomImage from '@s-ui/react-atom-image' /> ``` +### With a title + +```javascript + +``` + ### With skeleton while loading ```javascript ``` @@ -42,6 +53,7 @@ import AtomImage from '@s-ui/react-atom-image' ``` @@ -52,6 +64,7 @@ import AtomImage from '@s-ui/react-atom-image' ``` @@ -62,6 +75,7 @@ import AtomImage from '@s-ui/react-atom-image' @@ -75,6 +89,7 @@ Loads 50x50 image when the viewport is under 480px, elsewise it loads a 150x150 { const imageRef = useRef() @@ -78,12 +80,6 @@ const AtomImage = ({ backgroundImage: `url(${placeholder || skeleton})` } - const SpinnerExtended = - Spinner && - cloneElement(Spinner, { - className: CLASS_SPINNER - }) - return (
- {!error && isLoading && SpinnerExtended} + {!error && isLoading && spinner && ( + {spinner} + )} {error && ( )} @@ -148,7 +146,7 @@ AtomImage.propTypes = { skeleton: PropTypes.string, /** Spinner (component) displayed while the final image is being loaded */ - spinner: PropTypes.oneOfType([PropTypes.element, PropTypes.func]), + spinner: PropTypes.node, /** Icon (component) to be displayed in an Error Box when the image cannot be loaded */ errorIcon: PropTypes.oneOfType([PropTypes.element, PropTypes.func]), diff --git a/components/atom/image/src/types.js b/components/atom/image/src/types.js index 8f121030f6..9ecf420d98 100644 --- a/components/atom/image/src/types.js +++ b/components/atom/image/src/types.js @@ -11,6 +11,7 @@ const htmlImgProps = { referrerpolicy: PropTypes.string, sizes: PropTypes.string, srcset: PropTypes.string, + title: PropTypes.string, usemap: PropTypes.string, width: PropTypes.string } diff --git a/components/atom/input/CHANGELOG.md b/components/atom/input/CHANGELOG.md index 8fb06f2a0c..52d0ebbfeb 100644 --- a/components/atom/input/CHANGELOG.md +++ b/components/atom/input/CHANGELOG.md @@ -1,5 +1,14 @@ # CHANGELOG +# 5.22.0 (2022-10-04) + + +### Bug Fixes + +* **components/atom/input:** Add input validator function for input type number ([c72b340](https://github.com/SUI-Components/sui-components/commit/c72b3402d65cad1a467828c4b9de06de29603fb4)) + + + # 5.21.0 (2022-08-26) diff --git a/components/atom/input/demo/index.js b/components/atom/input/demo/index.js index 0ec18c7d12..670911343d 100644 --- a/components/atom/input/demo/index.js +++ b/components/atom/input/demo/index.js @@ -1,17 +1,9 @@ -import {useState} from 'react' - -import AtomInput, { - inputShapes, - inputSizes, - inputStates, - inputTypes -} from 'components/atom/input/src/index.js' +import {Fragment, useState} from 'react' import { Anchor, AntDesignIcon, Article, - Box, Button, Cell, Code, @@ -29,6 +21,12 @@ import { UnorderedList } from '@s-ui/documentation-library' +import AtomInput, { + inputShapes, + inputSizes, + inputStates, + inputTypes +} from '../../input/src/index.js' import {flexCenteredStyle, stackMap} from './settings.js' const DefaultDemo = () => ( @@ -166,7 +164,7 @@ const TypeDemo = () => { 'MASK', { type: inputTypes.MASK, - mask: 'ES00 0000 0000 00 0000000000', + mask: {mask: 'ES00 0000 0000 00 0000000000'}, placeholder: 'ES00 0000 0000 00 0000000000', charsSize: 31 }, @@ -429,7 +427,7 @@ const BorderlessDemo = () => { const [border, setBorder] = useState(true) const [mode, setMode] = useState('light') return ( -
+

No border

The border of the input can be removed using the boolean prop{' '} @@ -453,9 +451,7 @@ const BorderlessDemo = () => { /> - - - +
@@ -525,7 +521,9 @@ const InlineFormDemo = () => ( Input have its own way of provide a submision using the{' '} button prop, you can pass a React node. - Send} /> + Send} + />
) @@ -562,7 +560,7 @@ const ShapeDemo = () => ( ))} {Object.entries({default: undefined, ...inputSizes}).map( ([sizeKey, sizeValue]) => ( - <> + @@ -578,7 +576,7 @@ const ShapeDemo = () => ( ) )} - + ) )} diff --git a/components/atom/input/package.json b/components/atom/input/package.json index c07fe87751..c1a80955d0 100644 --- a/components/atom/input/package.json +++ b/components/atom/input/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-input", - "version": "5.21.0", + "version": "5.21.0-beta.4", "description": "", "main": "lib/index.js", "scripts": { @@ -11,7 +11,7 @@ "dependencies": { "@s-ui/react-hooks": "1", "@s-ui/react-primitive-polymorphic-element": "1", - "imask": "3.4.0" + "react-imask": "6.4.3" }, "peerDependencies": { "@s-ui/component-dependencies": "1" diff --git a/components/atom/input/src/Input/Component/index.scss b/components/atom/input/src/Input/Component/index.scss index 17bfbf0236..3fd7e18269 100644 --- a/components/atom/input/src/Input/Component/index.scss +++ b/components/atom/input/src/Input/Component/index.scss @@ -7,77 +7,29 @@ $class-read-only: '#{$base-class-input}--readOnly'; -webkit-appearance: none; @include sui-atom-input-input; - border-radius: $bdrs-atom-input; + background-color: transparent; min-height: auto; text-overflow: $tov-atom-input-placeholder; - &--charsSize { + padding-left: 0; + padding-right: 0; + border: 0 solid transparent; + &#{$base-class-input}--charsSize { width: inherit; } - &--hidden { + &#{$base-class-input}--hidden { display: none; } - &--readOnly { - background: $bgc-atom-input-read-only; - border: $bd-atom-input-read-only; - color: $c-atom-input-read-only; - } - - @include atom-input-shape-wrapper( - $base-class, - $base-class-input, - $bdrs-atom-input-shapes, - $sizes-atom-input, - $bdrs-atom-input, - $h-atom-input--m - ); - - &#{$base-class-input}--noBorder, - &#{$base-class-input}--noBorder:disabled { - border: 0; - &:focus { - border: 0; - box-shadow: none; - outline: 0; - } - } - &::placeholder { color: $c-atom-input-placeholder; } - &:disabled { - pointer-events: none; - - &:not(#{$class-read-only}) { - -webkit-text-fill-color: $c-atom-input-disabled; - - background: $bgc-atom-input-disabled; - border: $bd-atom-input-disabled; - color: $c-atom-input-disabled; - } - } - &:focus { - @include sui-atom-input-input-focus; - } - - @each $type, $attr in $sizes-atom-input { - &-size-#{$type} { - height: $attr; - min-height: $attr; - } + box-shadow: none; } - - @each $state, $color in $states-atom-input { - &.sui-AtomInput-input--#{$state} { - border-color: $color; - - &:focus { - box-shadow: $bxsh-atom-input-size $color; - } - } + &:focus-visible { + outline: 0; } &[type='number'] { diff --git a/components/atom/input/src/Input/Wrappers/Addons/InputAddons.js b/components/atom/input/src/Input/Wrappers/Addons/InputAddons.js index af4999be14..8fb49f887e 100644 --- a/components/atom/input/src/Input/Wrappers/Addons/InputAddons.js +++ b/components/atom/input/src/Input/Wrappers/Addons/InputAddons.js @@ -1,21 +1,14 @@ -import cx from 'classnames' import PropTypes from 'prop-types' import {INPUT_SHAPES, SIZES} from '../../../config.js' -import {ADDON_TYPES, BASE_CLASS_ADDON_WRAPPER, getClassName} from './config.js' +import {ADDON_TYPES, getClassName} from './config.js' const InputAddon = ({leftAddon, rightAddon, shape, size, children}) => { if (!(leftAddon || rightAddon)) { return children } return ( -
+ <> {leftAddon && ( {leftAddon} @@ -27,7 +20,7 @@ const InputAddon = ({leftAddon, rightAddon, shape, size, children}) => { {rightAddon} )} -
+ ) } diff --git a/components/atom/input/src/Input/Wrappers/Addons/config.js b/components/atom/input/src/Input/Wrappers/Addons/config.js index f561c5393b..2f4478da23 100644 --- a/components/atom/input/src/Input/Wrappers/Addons/config.js +++ b/components/atom/input/src/Input/Wrappers/Addons/config.js @@ -1,13 +1,12 @@ import cx from 'classnames' -import {BASE} from '../../../config.js' +import {BASE, BASE_CLASS_ITEM} from '../../../config.js' export const BASE_CLASS_ADDON = `${BASE}--withAddon` -export const BASE_CLASS_ADDON_WRAPPER = `${BASE_CLASS_ADDON}Wrapper` export const ADDON_TYPES = { LEFT: 'left', RIGHT: 'right' } export const getClassName = ({type}) => - cx(BASE_CLASS_ADDON, `${BASE_CLASS_ADDON}--${type}`) + cx(BASE_CLASS_ITEM, BASE_CLASS_ADDON, `${BASE_CLASS_ADDON}--${type}`) diff --git a/components/atom/input/src/Input/Wrappers/Addons/index.scss b/components/atom/input/src/Input/Wrappers/Addons/index.scss index e66319f7d0..aacf641e4e 100644 --- a/components/atom/input/src/Input/Wrappers/Addons/index.scss +++ b/components/atom/input/src/Input/Wrappers/Addons/index.scss @@ -1,34 +1,56 @@ -#{$base-class}--withAddon { - align-items: center; - background-color: $bgc-atom-input-addon; - border-color: $bdc-atom-input-addon; - border-style: solid; - color: $c-atom-input-addon; - display: flex; - flex-direction: column; - height: inherit; - justify-content: center; - line-height: inherit; - padding-left: $p-atom-input-addon-left; - padding-right: $p-atom-input-addon-right; +$base-class-addon: '#{$base-class}--withAddon'; - &--right { - border-width: $bdw-atom-input-addon-right; - } +#{$base-class} { + #{$base-class-addon} { + align-items: center; + background-color: $bgc-atom-input-addon; + border-color: $bdc-atom-input-addon; + border-style: solid; + color: $c-atom-input-addon; + display: flex; + flex-direction: column; + justify-content: center; + line-height: inherit; + padding-left: $p-atom-input-addon-left; + padding-right: $p-atom-input-addon-right; + width: auto; - &--left { - border-width: $bdw-atom-input-addon-left; - } + &#{$base-class-addon}--right { + border-left-width: 0; + } - &Wrapper { - display: flex; - @include atom-input-shape-wrapper( - $base-class, - #{'.sui-AtomInput--withAddonWrapper'}, - $bdrs-atom-input-shapes, - $sizes-atom-input, - $bdrs-atom-input, - $h-atom-input--m - ); + &#{$base-class-addon}--left { + border-right-width: 0; + } + } + @each $state, $color in $states-atom-input { + &#{$base-class}--status-#{$state} { + #{$base-class}-item { + &#{$base-class-addon} { + border-color: $bdc-atom-input-addon; + &#{$base-class-addon}--right { + border-left-color: $color; + } + &#{$base-class-addon}--left { + border-right-color: $color; + } + } + } + } + } + @each $state, $color in $states-atom-input { + &#{$base-class}--status-#{$state} { + #{$base-class}-item { + &#{$base-class-addon} { + border-color: $bdc-atom-input-addon; + &#{$base-class-addon}--right { + border-left-color: $color; + } + &#{$base-class-addon}--left { + border-right-color: $color; + } + } + } + } } } diff --git a/components/atom/input/src/Input/Wrappers/Button/InputButton.js b/components/atom/input/src/Input/Wrappers/Button/InputButton.js index fc6a62a687..87d1d5626a 100644 --- a/components/atom/input/src/Input/Wrappers/Button/InputButton.js +++ b/components/atom/input/src/Input/Wrappers/Button/InputButton.js @@ -1,6 +1,7 @@ +import cx from 'classnames' import PropTypes from 'prop-types' -import {BASE_CLASS_BUTTON} from './config.js' +import {BASE_CLASS_BUTTON, BASE_CLASS_ITEM} from './config.js' const InputButton = ({button, children}) => { if (button === undefined) { @@ -8,10 +9,12 @@ const InputButton = ({button, children}) => { } return ( -
-
{children}
-
{button}
-
+ <> + {children} + + {button} + + ) } InputButton.propTypes = { diff --git a/components/atom/input/src/Input/Wrappers/Button/config.js b/components/atom/input/src/Input/Wrappers/Button/config.js index 86a66b2c31..da59c18f38 100644 --- a/components/atom/input/src/Input/Wrappers/Button/config.js +++ b/components/atom/input/src/Input/Wrappers/Button/config.js @@ -1,3 +1,4 @@ -import {BASE} from '../../../config.js' +import {BASE, BASE_CLASS_ITEM} from '../../../config.js' +export {BASE_CLASS_ITEM} export const BASE_CLASS_BUTTON = `${BASE}--withButton` diff --git a/components/atom/input/src/Input/Wrappers/Button/index.scss b/components/atom/input/src/Input/Wrappers/Button/index.scss index ca26941a34..5274fe8589 100644 --- a/components/atom/input/src/Input/Wrappers/Button/index.scss +++ b/components/atom/input/src/Input/Wrappers/Button/index.scss @@ -1,13 +1,3 @@ -#{$base-class}--withButton { - display: flex; - width: 100%; - - &-input { - flex: 1; - width: 100%; - } - - &-button { - margin-left: $m-m; - } +#{$base-class}--withButton-button { + overflow: hidden; } diff --git a/components/atom/input/src/Input/Wrappers/Icons/InputIcons.js b/components/atom/input/src/Input/Wrappers/Icons/InputIcons.js index 22010b5f94..ce09bab509 100644 --- a/components/atom/input/src/Input/Wrappers/Icons/InputIcons.js +++ b/components/atom/input/src/Input/Wrappers/Icons/InputIcons.js @@ -2,13 +2,11 @@ import cx from 'classnames' import PropTypes from 'prop-types' import { - BASE_CLASS_ICON, BASE_CLASS_ICON_COMPONENT, BASE_CLASS_ICON_COMPONENT_HANDLER, BASE_CLASS_ICON_COMPONENT_LEFT, BASE_CLASS_ICON_COMPONENT_RIGHT, - BASE_CLASS_ICON_LEFT, - BASE_CLASS_ICON_RIGHT + BASE_CLASS_ICON_CONTENT_COMPONENT } from './config.js' const InputIcons = ({ @@ -18,9 +16,6 @@ const InputIcons = ({ onClickRightIcon, children }) => { - if (!(leftIcon || rightIcon)) { - return children - } const handleLeftClick = event => { onClickLeftIcon && onClickLeftIcon(event) } @@ -30,24 +25,22 @@ const InputIcons = ({ } return ( -
+ <> {leftIcon && ( - {leftIcon} + + {leftIcon} + )} {children} @@ -55,17 +48,20 @@ const InputIcons = ({ - {rightIcon} + + {rightIcon} + )} -
+ ) } diff --git a/components/atom/input/src/Input/Wrappers/Icons/config.js b/components/atom/input/src/Input/Wrappers/Icons/config.js index 4d415eb0d6..73e045e57b 100644 --- a/components/atom/input/src/Input/Wrappers/Icons/config.js +++ b/components/atom/input/src/Input/Wrappers/Icons/config.js @@ -1,14 +1,18 @@ -import {BASE} from '../../../config.js' +import { + BASE, + BASE_CLASS_AREA_FOCUSABLE, + BASE_CLASS_ITEM +} from '../../../config.js' export const ICON_TYPES = { LEFT: 'left', RIGHT: 'right' } +export {BASE_CLASS_ITEM, BASE_CLASS_AREA_FOCUSABLE} export const BASE_CLASS_ICON = `${BASE}--withIcon` -export const BASE_CLASS_ICON_LEFT = `${BASE_CLASS_ICON}--${ICON_TYPES.LEFT}` -export const BASE_CLASS_ICON_RIGHT = `${BASE_CLASS_ICON}--${ICON_TYPES.RIGHT}` export const BASE_CLASS_ICON_COMPONENT = `${BASE_CLASS_ICON}-icon` +export const BASE_CLASS_ICON_CONTENT_COMPONENT = `${BASE_CLASS_ICON_COMPONENT}--content` export const BASE_CLASS_ICON_COMPONENT_HANDLER = `${BASE_CLASS_ICON_COMPONENT}--withHandler` export const BASE_CLASS_ICON_COMPONENT_LEFT = `${BASE_CLASS_ICON_COMPONENT}--${ICON_TYPES.LEFT}` export const BASE_CLASS_ICON_COMPONENT_RIGHT = `${BASE_CLASS_ICON_COMPONENT}--${ICON_TYPES.RIGHT}` diff --git a/components/atom/input/src/Input/Wrappers/Icons/index.scss b/components/atom/input/src/Input/Wrappers/Icons/index.scss index f493df3295..f2e5d51fb6 100644 --- a/components/atom/input/src/Input/Wrappers/Icons/index.scss +++ b/components/atom/input/src/Input/Wrappers/Icons/index.scss @@ -1,25 +1,14 @@ -#{$base-class}--withIcon { - position: relative; - width: 100%; +@use 'sass:math'; - &--left #{$base-class-input} { - padding-left: $pl-atom-input-input; - } - - &--right #{$base-class-input} { - padding-right: $pr-atom-input-input; - } +$base-class-with-icon: '#{$base-class}--withIcon'; - &-icon { +#{$base-class} { + #{$base-class-with-icon}-icon { align-items: center; color: $c-atom-input-icon; display: flex; fill: $c-atom-input-icon; - height: $w-atom-input-icon; justify-content: center; - position: absolute; - top: $t-atom-input-icon; - transform: translateY($trf-ty-atom-input-icon); width: $w-atom-input-icon; pointer-events: none; @@ -29,16 +18,25 @@ } &--left { - left: $l-atom-input-icon; + #{$base-class-with-icon}-icon--content { + width: $w-atom-input-icon; + } } &--right { - right: $r-atom-input-icon; + #{$base-class-with-icon}-icon--content { + width: $w-atom-input-icon; + } } - - & > * { - height: 100%; - width: 100%; + } + @each $state, $color in $states-atom-input { + &#{$base-class}--status-#{$state} { + #{$base-class-with-icon}-icon { + #{$base-class-with-icon}-icon--content { + color: $color; + fill: $color; + } + } } } } diff --git a/components/atom/input/src/Input/index.js b/components/atom/input/src/Input/index.js index 1bdf066e20..c7e7348592 100644 --- a/components/atom/input/src/Input/index.js +++ b/components/atom/input/src/Input/index.js @@ -1,16 +1,31 @@ import {forwardRef} from 'react' +import cx from 'classnames' import PropTypes from 'prop-types' -import {SIZES} from '../config.js' -import Input, {inputSizes, inputStates} from './Component/index.js' +import { + BASE_CLASS_AREA_FOCUSABLE, + BASE_CLASS_ITEM, + SIZES, + TYPES +} from '../config.js' +import Mask from '../Mask/index.js' +import Password from '../Password/index.js' +import Input from './Component/index.js' import InputAddons from './Wrappers/Addons/InputAddons.js' import InputButton from './Wrappers/Button/InputButton.js' import InputIcons from './Wrappers/Icons/InputIcons.js' +const componentType = { + undefined: props => [Input, props], + [TYPES.SUI_PASSWORD]: ({type, ...props}) => [Password, {...props}], + [TYPES.MASK]: ({type, ...props}) => [Mask, {...props}] +} + const BaseInput = forwardRef( ( { + type, button, leftAddon, rightAddon, @@ -24,6 +39,10 @@ const BaseInput = forwardRef( }, forwardedRef ) => { + const [Component, passedProps] = componentType[type] + ? componentType[type]({type, size, ...inputProps}) + : componentType[undefined]({type, size, ...inputProps}) + return ( - - - {children} - - + + + + {children} + + + ) @@ -49,6 +70,8 @@ const BaseInput = forwardRef( ) BaseInput.propTypes = { + /* text, password, date or number */ + type: PropTypes.string, /** button html element */ button: PropTypes.node, /* inner react node element */ @@ -70,4 +93,4 @@ BaseInput.propTypes = { } export default BaseInput -export {inputSizes, inputStates, BaseInput} +export {BaseInput} diff --git a/components/atom/input/src/Mask/index.js b/components/atom/input/src/Mask/index.js index 1259d696a3..c79c26becf 100644 --- a/components/atom/input/src/Mask/index.js +++ b/components/atom/input/src/Mask/index.js @@ -1,50 +1,44 @@ -import {forwardRef, useEffect, useRef, useState} from 'react' +import {forwardRef} from 'react' import PropTypes from 'prop-types' -import Input from '../Input/index.js' +import Input from '../Input/Component/index.js' + +import useMask from './useMask.js' const MaskInput = forwardRef( - ({name, onChange, mask: maskOptions, ...props}, forwardedRef) => { - const [mask, setMask] = useState(null) - const refInput = useRef(null) - - useEffect(() => () => mask && mask.destroy(), [mask]) - - const handleChange = (ev, {value}) => { - typeof onChange === 'function' && onChange(ev, {value}) - } - - const handleFocus = () => { - if (!mask) { - import('imask').then(({default: IMask}) => { - setMask(new IMask(refInput.current, maskOptions)) - }) - } - } - - return ( - - ) + ( + {name, onChange, onComplete, mask, value, defaultValue, ...props}, + forwardedRef + ) => { + const {maskedValue, ref} = useMask({ + value, + defaultValue, + mask, + onChange, + onComplete, + forwardedRef + }) + + return } ) MaskInput.displayName = 'MaskInput' MaskInput.propTypes = { + /* The value of the control */ + value: PropTypes.string, + /* default value of the control */ + defaultValue: PropTypes.string, /* mask object, see https://unmanner.github.io/imaskjs/ */ mask: PropTypes.object.isRequired, /* The name of the control */ name: PropTypes.string, /* Event launched on every input change */ - onChange: PropTypes.func + onChange: PropTypes.func, + /* Event fired every onChange which completes teh mask */ + onComplete: PropTypes.func } export default MaskInput diff --git a/components/atom/input/src/Mask/useMask.js b/components/atom/input/src/Mask/useMask.js new file mode 100644 index 0000000000..d7d6327743 --- /dev/null +++ b/components/atom/input/src/Mask/useMask.js @@ -0,0 +1,43 @@ +import {useEffect} from 'react' + +import {useIMask} from 'react-imask' + +import useMergeRefs from '@s-ui/react-hooks/lib/useMergeRefs' +import useMountedState from '@s-ui/react-hooks/lib/useMountedState' + +import {isFunction} from '../config.js' + +const useMask = ({ + value: argValue, + defaultValue: argDefaultValue, + mask, + onChange, + onComplete, + forwardedRef +}) => { + const [value] = useMountedState(argValue, argDefaultValue) + const { + ref: refInput, + value: maskedValue = '', + setValue + } = useIMask( + {...mask}, + { + onAccept: (value, maskRef, event, ...args) => + isFunction(onChange) && onChange(event, {value, maskRef, ...args}), + onComplete: (value, maskRef, event, ...args) => + isFunction(onComplete) && onComplete(event, {value, maskRef, ...args}) + } + ) + useEffect(() => { + if (value !== maskedValue) { + setValue(value) + } + }, [argValue, setValue, maskedValue]) + + const ref = useMergeRefs(refInput, forwardedRef) + + return Object.assign([maskedValue, ref]) +} + +export default useMask diff --git a/components/atom/input/src/Password/config.js b/components/atom/input/src/Password/config.js index c18b7ba0a2..e2bec087f5 100644 --- a/components/atom/input/src/Password/config.js +++ b/components/atom/input/src/Password/config.js @@ -1,6 +1,7 @@ -import {BASE_CLASS} from '../config.js' +import {BASE, BASE_CLASS_ITEM} from '../config.js' -export const BASE_CLASS_PASSWORD = `${BASE_CLASS}-password` +export {BASE_CLASS_ITEM} +export const BASE_CLASS_PASSWORD = `${BASE}-password` export const BASE_CLASS_PASSWORD_TOGGLE_BUTTON = `${BASE_CLASS_PASSWORD}--toggleButton` export const TEXT = 'text' diff --git a/components/atom/input/src/Password/index.js b/components/atom/input/src/Password/index.js index f26592e398..e6e4a69912 100644 --- a/components/atom/input/src/Password/index.js +++ b/components/atom/input/src/Password/index.js @@ -1,16 +1,12 @@ import {forwardRef, useState} from 'react' +import cx from 'classnames' import PropTypes from 'prop-types' import useControlledState from '@s-ui/react-hooks/lib/useControlledState' -import Input from '../Input/index.js' -import { - BASE_CLASS_PASSWORD, - BASE_CLASS_PASSWORD_TOGGLE_BUTTON, - PASSWORD, - TEXT -} from './config.js' +import Input from '../Input/Component/index.js' +import {BASE_CLASS_PASSWORD_TOGGLE_BUTTON, PASSWORD, TEXT} from './config.js' const Password = forwardRef( ( @@ -38,7 +34,7 @@ const Password = forwardRef( } return ( -
+ <> -
+ {type === PASSWORD ? pwShowLabel : pwHideLabel} -
-
+ + ) } ) diff --git a/components/atom/input/src/Password/styles/index.scss b/components/atom/input/src/Password/styles/index.scss index 6da24f47f1..ce2e2dbb0c 100644 --- a/components/atom/input/src/Password/styles/index.scss +++ b/components/atom/input/src/Password/styles/index.scss @@ -1,16 +1,11 @@ -$base-class-password: '#{$base-class-input}-password'; +$base-class-password: '#{$base-class}-password'; #{$base-class-password} { - position: relative; - width: 100%; - &--toggleButton { color: $c-atom-input-password-toggle-button; cursor: pointer; - position: absolute; - right: $r-atom-input-password-toggle-button; - top: 50%; - transform: translateY(-50%); user-select: none; + display: flex; + align-items: center; } } diff --git a/components/atom/input/src/config.js b/components/atom/input/src/config.js index 75c4f7aef1..97ad8f508e 100644 --- a/components/atom/input/src/config.js +++ b/components/atom/input/src/config.js @@ -6,6 +6,8 @@ export const COMPONENT = 'Input' export const BASE = `${PREFIX}-${CATEGORY}${COMPONENT}` +export const BASE_CLASS_ITEM = `${BASE}-item` +export const BASE_CLASS_AREA_FOCUSABLE = `${BASE}-area-focusable` export const BASE_CLASS = `${BASE}-input` // Enums @@ -59,9 +61,14 @@ export const getClassNames = ({ hideInput && `${BASE_CLASS}--hidden`, noBorder && `${BASE_CLASS}--noBorder`, readOnly && `${BASE_CLASS}--readOnly`, - errorState && `${BASE_CLASS}--${INPUT_STATES.ERROR}`, - errorState === false && `${BASE_CLASS}--${INPUT_STATES.SUCCESS}`, - state && `${BASE_CLASS}--${state}`, + errorState && `${BASE_CLASS}--status-${INPUT_STATES.ERROR}`, + errorState === false && `${BASE_CLASS}--status-${INPUT_STATES.SUCCESS}`, + state && `${BASE_CLASS}--status-${state}`, shape && `${BASE_CLASS}-shape-${shape}` ) } + +export const isFunction = fn => typeof fn === 'function' + +export const isValidSize = charSize => + Number.isInteger(charSize) && charSize >= 0 diff --git a/components/atom/input/src/helper.js b/components/atom/input/src/helper.js new file mode 100644 index 0000000000..882d809f56 --- /dev/null +++ b/components/atom/input/src/helper.js @@ -0,0 +1,7 @@ +export const checkIfValidNumberInput = event => { + // Check if input type number is valid as input type number doesn't currently work in browsers like Safari and Firefox + // Allowing: Integers | Backspace | Tab | Delete | Left & Right arrow keys + const allowedCharacter = /(^\d*$)|(Backspace|Tab|Delete|ArrowLeft|ArrowRight)/ + + return !event.key.match(allowedCharacter) && event.preventDefault() +} diff --git a/components/atom/input/src/index.js b/components/atom/input/src/index.js index 382998bf1d..ba3a1a5017 100644 --- a/components/atom/input/src/index.js +++ b/components/atom/input/src/index.js @@ -1,22 +1,61 @@ import {forwardRef} from 'react' +import cx from 'classnames' import PropTypes from 'prop-types' -import Input, {inputSizes, inputStates} from './Input/index.js' -import Mask from './Mask/index.js' -import Password from './Password/index.js' -import {INPUT_SHAPES, TYPES} from './config.js' - -const AtomInput = forwardRef(({type, ...props}, ref) => { - switch (type) { - case 'sui-password': - return - case 'mask': - return - default: - return - } -}) +import Input from './Input/index.js' +import {BASE, INPUT_SHAPES, INPUT_STATES, SIZES, TYPES, isValidSize} from './config.js' +import {checkIfValidNumberInput} from './helper.js' + +const AtomInput = forwardRef(({ + type, + shape, + charsSize, + size = SIZES.MEDIUM, + noBorder, + errorState, + state, + disabled, + readOnly, + ...props + }, + ref + ) => { + return ( + + + + ) + } +) AtomInput.propTypes = { /** native types (text, date, ...), 'sui-password' */ @@ -77,7 +116,7 @@ AtomInput.propTypes = { disabled: PropTypes.bool, /** 's' or 'm', default: 'm' */ - size: PropTypes.oneOf(Object.values(inputSizes)), + size: PropTypes.oneOf(Object.values(SIZES)), /** width of input based in number of characters (native "size" attribute) */ charsSize: PropTypes.number, @@ -107,7 +146,7 @@ AtomInput.propTypes = { errorState: PropTypes.bool, /** 'success', 'error' or 'alert' */ - state: PropTypes.oneOf(Object.values(inputStates)), + state: PropTypes.oneOf(Object.values(INPUT_STATES)), /** value of the control */ value: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), @@ -131,7 +170,13 @@ AtomInput.propTypes = { inputMode: PropTypes.string, /** Sets the shape of the input field. It can be 'rounded', 'square' or 'circle' */ - shape: PropTypes.string + shape: PropTypes.string, + + /** Wether to hide the input border or not */ + noBorder: PropTypes.bool, + + /* This Boolean attribute prevents the user from interacting with the input but without disabled styles */ + readOnly: PropTypes.bool } AtomInput.displayName = 'AtomInput' @@ -139,8 +184,8 @@ AtomInput.displayName = 'AtomInput' export default AtomInput export { - inputSizes, - inputStates, + SIZES as inputSizes, + INPUT_STATES as inputStates, TYPES as inputTypes, INPUT_SHAPES as inputShapes } diff --git a/components/atom/input/src/mixins.scss b/components/atom/input/src/mixins.scss deleted file mode 100644 index 9985338e36..0000000000 --- a/components/atom/input/src/mixins.scss +++ /dev/null @@ -1,75 +0,0 @@ -@mixin atom-input-shape-wrapper( - $baseClass, - $baseClassName, - $shapeMap, - $sizesMap, - $bdrs-defaultValue, - $sz-defaultValue -) { - $this: &; - @if #{'sui-AtomInput-input'} == $baseClassName { - @each $shapeMapKey, $shapeMapValue in $shapeMap { - @if $shapeMapKey != 'circle' { - &#{$baseClassName}-shape-#{$shapeMapKey} { - border-radius: $shapeMapValue; - } - } @else { - @each $sizeKey, $sizeValue in $sizesMap { - &#{$baseClassName}-shape-#{$shapeMapKey}#{$baseClassName}-size-#{$sizeKey} { - border-radius: $sizeValue * 0.5; - } - } - } - } - } @else { - > *, - #{$baseClass}-input { - border-radius: 0; - } - > *:first-child { - border-top-left-radius: $bdrs-defaultValue; - border-bottom-left-radius: $bdrs-defaultValue; - } - > *:last-child { - border-top-right-radius: $bdrs-defaultValue; - border-bottom-right-radius: $bdrs-defaultValue; - } - @each $shapeMapKey, $shapeMapValue in $shapeMap { - &#{$baseClassName}-shape-#{$shapeMapKey} { - > *, - #{$baseClass}-input { - border-radius: 0; - } - @if $shapeMapKey != 'circle' { - border-radius: $shapeMapValue; - > *:first-child { - border-top-left-radius: $shapeMapValue; - border-bottom-left-radius: $shapeMapValue; - } - > *:last-child { - border-top-right-radius: $shapeMapValue; - border-bottom-right-radius: $shapeMapValue; - } - } @else { - @each $sizeKey, $sizeValue in $sizesMap { - &#{$baseClassName}-shape-#{$shapeMapKey}#{$baseClassName}-size-#{$sizeKey} { - border-radius: $sizeValue * 0.5; - > *, - #{$baseClass}-input { - border-radius: 0; - } - > *:first-child { - border-top-left-radius: $sizeValue * 0.5; - border-bottom-left-radius: $sizeValue * 0.5; - } - > *:last-child { - border-top-right-radius: $sizeValue * 0.5; - border-bottom-right-radius: $sizeValue * 0.5; - } - } - } - } - } - } - } -} diff --git a/components/atom/input/src/styles/index.scss b/components/atom/input/src/styles/index.scss index 6d42d354f5..cb92baa520 100644 --- a/components/atom/input/src/styles/index.scss +++ b/components/atom/input/src/styles/index.scss @@ -1,5 +1,115 @@ +@use 'sass:math'; + $base-class: '.sui-AtomInput'; +$base-class-area-focusable: '.sui-AtomInput-area-focusable'; @import '../Input/Component/index'; @import '../Input/Wrappers/index'; @import '../Password/index'; + +#{$base-class} { + display: flex; + align-items: stretch; + flex-grow: 1; + + #{$base-class}-item { + border: $bd-atom-input-base; + &:first-child { + border-bottom-left-radius: $bdrs-atom-input; + border-top-left-radius: $bdrs-atom-input; + border-left-width: $bdw-s; + } + &:last-child { + border-bottom-right-radius: $bdrs-atom-input; + border-top-right-radius: $bdrs-atom-input; + border-right-width: $bdw-s; + } + } + + &:not(#{$base-class}--has-charSize) { + #{$base-class-area-focusable} { + flex-grow: 1; + } + } + + @each $shapeMapKey, $shapeMapValue in $bdrs-atom-input-shapes { + &#{$base-class}-shape-#{$shapeMapKey} { + #{$base-class}-item { + &:first-child { + border-bottom-left-radius: $shapeMapValue; + border-top-left-radius: $shapeMapValue; + } + &:last-child { + border-bottom-right-radius: $shapeMapValue; + border-top-right-radius: $shapeMapValue; + } + } + } + } + + #{$base-class-area-focusable} { + display: flex; + padding-left: $pl-atom-input; + padding-right: $pr-atom-input; + background: $bgc-atom-input; + gap: math.div($p-l, 2); + &:focus-within { + border: $bd-atom-input-focus; + box-shadow: $bxsh-atom-input; + outline: 0 none; + } + } + + @each $state, $color in $states-atom-input { + &#{$base-class}--status-#{$state} { + #{$base-class}-item { + border-color: $color; + } + } + } + + &#{$base-class}--is-read-only { + #{$base-class-area-focusable} { + background: $bgc-atom-input-read-only; + border: $bd-atom-input-read-only; + color: $c-atom-input-read-only; + } + } + + &#{$base-class}--is-disabled { + #{$base-class-area-focusable} { + pointer-events: none; + } + &:not(#{$base-class}--is-read-only) { + #{$base-class-area-focusable} { + -webkit-text-fill-color: $c-atom-input-disabled; + background: $bgc-atom-input-disabled; + border: $bd-atom-input-disabled; + color: $c-atom-input-disabled; + } + } + } + + @each $type, $attr in $sizes-atom-input { + &#{$base-class}-size-#{$type} { + height: $attr; + min-height: $attr; + } + } + + &#{$base-class}-borderless { + &, + &#{$base-class}--is-disabled:not(#{$base-class}--is-read-only) { + #{$base-class}-item { + border-width: 0; + } + #{$base-class-area-focusable} { + padding-left: $pl-atom-input + $bdw-s; + padding-right: $pr-atom-input + $bdw-s; + &:focus-within { + box-shadow: none; + } + } + } + } +} diff --git a/components/atom/input/src/styles/settings.scss b/components/atom/input/src/styles/settings.scss index 13012356cd..72501a9dc4 100644 --- a/components/atom/input/src/styles/settings.scss +++ b/components/atom/input/src/styles/settings.scss @@ -1,3 +1 @@ @import '../settings.scss'; - -@import '../mixins.scss'; diff --git a/components/atom/label/CHANGELOG.md b/components/atom/label/CHANGELOG.md index e971a2468a..6b0068587a 100644 --- a/components/atom/label/CHANGELOG.md +++ b/components/atom/label/CHANGELOG.md @@ -1,5 +1,16 @@ # CHANGELOG +# 1.22.0 (2022-09-29) + + +### Features + +* **components/atom/label:** add opacity token ([#2338](https://github.com/SUI-Components/sui-components/issues/2338)) ([56540fa](https://github.com/SUI-Components/sui-components/commit/56540fa9d5ea7625eb91d273ab87c5f7285f6bd4)) +* **Root:** Delete undefined dependencies ([c145905](https://github.com/SUI-Components/sui-components/commit/c145905350328925ba6fda2a462d7f8b508c8ea0)) +* **Root:** Merge commit ([d3735d0](https://github.com/SUI-Components/sui-components/commit/d3735d0644332e674d5a5b6291680697f0d6f7c4)) + + + # 1.21.0 (2022-06-30) diff --git a/components/atom/label/package.json b/components/atom/label/package.json index 8daaaa0e40..4307553392 100644 --- a/components/atom/label/package.json +++ b/components/atom/label/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-label", - "version": "1.21.0", + "version": "1.22.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/atom/label/src/styles/index.scss b/components/atom/label/src/styles/index.scss index 4ae987b8bb..9e55e68aa3 100644 --- a/components/atom/label/src/styles/index.scss +++ b/components/atom/label/src/styles/index.scss @@ -40,7 +40,7 @@ $base-class: '.sui-AtomLabel'; &--disabled { cursor: default; - opacity: 0.3; + opacity: $o-atom-label-disabled; pointer-events: none; } diff --git a/components/atom/label/src/styles/settings.scss b/components/atom/label/src/styles/settings.scss index e5960402bc..6157875b58 100644 --- a/components/atom/label/src/styles/settings.scss +++ b/components/atom/label/src/styles/settings.scss @@ -2,6 +2,7 @@ $c-atom-label: inherit !default; $c-atom-label-optional: $c-gray-light !default; $c-atom-label-contrast: $c-white !default; $c-atom-label-disabled: inherit !default; +$o-atom-label-disabled: 0.3 !default; $fw-atom-label: inherit !default; $c-atom-label-type: success $c-success, error $c-error, alert $c-alert, contrast $c-atom-label-contrast, disabled $c-atom-label-disabled !default; diff --git a/components/atom/pinInput/CHANGELOG.md b/components/atom/pinInput/CHANGELOG.md index 28495d9be8..1c14c4970d 100644 --- a/components/atom/pinInput/CHANGELOG.md +++ b/components/atom/pinInput/CHANGELOG.md @@ -1,5 +1,15 @@ # CHANGELOG +# 1.11.0 (2022-10-11) + + +### Features + +* **components/atom/pinInput:** create new token to be able to overwrite the placeholder color ([513ae8a](https://github.com/SUI-Components/sui-components/commit/513ae8acc0728d6aa9e1a5780871985de2b52b3d)) +* **components/atom/pinInput:** replace inherit by auto ([c2f410e](https://github.com/SUI-Components/sui-components/commit/c2f410e589644bcbb1a26c5c107a73f336442d82)) + + + # 1.10.0 (2022-09-05) diff --git a/components/atom/pinInput/package.json b/components/atom/pinInput/package.json index 2a30c11ba6..107a9be070 100644 --- a/components/atom/pinInput/package.json +++ b/components/atom/pinInput/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-pin-input", - "version": "1.10.0", + "version": "1.11.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/atom/pinInput/src/PinInputField.scss b/components/atom/pinInput/src/PinInputField.scss index 9ab9a55566..0617600774 100644 --- a/components/atom/pinInput/src/PinInputField.scss +++ b/components/atom/pinInput/src/PinInputField.scss @@ -10,6 +10,10 @@ padding: 0; text-align: center; + &::placeholder { + color: $c-pin-input-placeholder; + } + &:focus { border: $bdw-s solid $c-primary-dark; cursor: text; diff --git a/components/atom/pinInput/src/config.scss b/components/atom/pinInput/src/config.scss index 6ddd06549d..ac371a24cd 100644 --- a/components/atom/pinInput/src/config.scss +++ b/components/atom/pinInput/src/config.scss @@ -4,6 +4,7 @@ $bdw-pin-input-field: $bdw-s !default; $fw-pin-input-field: $fw-regular !default; $lh-pin-input-field: $lh-m !default; $m-pin-input-children: $m-m !default; +$c-pin-input-placeholder: auto !default; $s-pin-input-field: ( // 64px diff --git a/components/atom/popover/CHANGELOG.md b/components/atom/popover/CHANGELOG.md index 403d0c5c29..e825a143e7 100644 --- a/components/atom/popover/CHANGELOG.md +++ b/components/atom/popover/CHANGELOG.md @@ -1,5 +1,18 @@ # CHANGELOG +# 3.13.0 (2022-10-07) + + +### Features + +* **components/atom/popover:** add icon color type ([a6dac91](https://github.com/SUI-Components/sui-components/commit/a6dac91162387b0aa7e5fb99557d76f061ba034b)) + + + +# 3.12.0 (2022-10-06) + + + # 3.11.0 (2022-06-20) diff --git a/components/atom/popover/demo/ArticleType.js b/components/atom/popover/demo/ArticleType.js index 6c6e07f4bb..1883d94147 100644 --- a/components/atom/popover/demo/ArticleType.js +++ b/components/atom/popover/demo/ArticleType.js @@ -1,32 +1,68 @@ import AtomPopover from 'components/atom/popover/src/index.js' import PropTypes from 'prop-types' -import {Article, Button, H2, Paragraph} from '@s-ui/documentation-library' +import IconClose from './Icons/IconClose.js' +import { + Article, + Button, + H2, + Paragraph, + Grid, + Cell +} from '@s-ui/documentation-library' const ArticleType = ({className, content: Content}) => { return ( -
-
-

Custom Types

- - You can even define some other extra types defining your own vertical - custom types and looping this defined keys through scss. - -
- - - - +
+

Custom Types

+ + You can even define some other extra types defining your own vertical + custom types and looping this defined keys through scss. + + + + } + content={Content} + hideArrow={false} + > + + + + + } + content={Content} + hideArrow={false} + > + + + +
) } diff --git a/components/atom/popover/demo/index.scss b/components/atom/popover/demo/index.scss index 4b02526904..4d4706421a 100644 --- a/components/atom/popover/demo/index.scss +++ b/components/atom/popover/demo/index.scss @@ -1,13 +1,21 @@ @import '~@s-ui/theme/lib/index'; +@import '../src/styles/settings'; + $popover-type: ( dark: ( bgc: $c-gray-dark-3, - bdc: $c-gray-dark-3 + bdc: $c-gray-dark-3, + c-icon: $c-white + ), + alert: ( + bgc: $c-alert-dark-3, + bdc: $c-alert-dark-3, + c-icon: $c-white ) ); -@import '../src/index'; +@import '../src/styles/index'; .DemoAtomPopover-section { .blink { diff --git a/components/atom/popover/package.json b/components/atom/popover/package.json index a3f91f9816..38568d53ad 100644 --- a/components/atom/popover/package.json +++ b/components/atom/popover/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-popover", - "version": "3.11.0", + "version": "3.13.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/atom/popover/src/styles/index.scss b/components/atom/popover/src/styles/index.scss index 27181642ae..c077193dc0 100644 --- a/components/atom/popover/src/styles/index.scss +++ b/components/atom/popover/src/styles/index.scss @@ -27,10 +27,16 @@ $class-arrow: '#{$base-class}-arrow'; @each $type-key, $type-value in $popover-type { $bgc: map-get($type-value, bgc); $bdc: map-get($type-value, bdc); + $c-icon: map-get($type-value, c-icon); &--type-#{$type-key} { background-color: $bgc; border-color: $bdc; + #{$base-class}-closeIcon { + svg { + fill: $c-icon; + } + } } } } @@ -43,7 +49,7 @@ $class-arrow: '#{$base-class}-arrow'; z-index: $z-tooltips; svg { - fill: $c-atom-popover-close-icon !important; + fill: $c-atom-popover-close-icon; height: $sz-atom-popover-close-icon; width: $sz-atom-popover-close-icon; } diff --git a/components/atom/progressBar/CHANGELOG.md b/components/atom/progressBar/CHANGELOG.md index 4c55159eae..344e25d3fa 100644 --- a/components/atom/progressBar/CHANGELOG.md +++ b/components/atom/progressBar/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# 2.9.0 (2022-10-06) + + + # 2.8.0 (2022-06-20) diff --git a/components/atom/progressBar/package.json b/components/atom/progressBar/package.json index 47e3d64066..93d78fe5df 100644 --- a/components/atom/progressBar/package.json +++ b/components/atom/progressBar/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-progress-bar", - "version": "2.8.0", + "version": "2.9.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/atom/radioButton/CHANGELOG.md b/components/atom/radioButton/CHANGELOG.md index e0f53f2f9e..8ea6a9c73e 100644 --- a/components/atom/radioButton/CHANGELOG.md +++ b/components/atom/radioButton/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# 1.10.0 (2022-10-06) + + + # 1.9.0 (2022-06-20) diff --git a/components/atom/radioButton/package.json b/components/atom/radioButton/package.json index cf7c144b0a..1adf827dc3 100644 --- a/components/atom/radioButton/package.json +++ b/components/atom/radioButton/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-radio-button", - "version": "1.9.0", + "version": "1.10.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/atom/skeleton/CHANGELOG.md b/components/atom/skeleton/CHANGELOG.md index ec75638849..c8496da3a9 100644 --- a/components/atom/skeleton/CHANGELOG.md +++ b/components/atom/skeleton/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# 1.6.0 (2022-10-06) + + + # 1.5.0 (2022-06-20) diff --git a/components/atom/skeleton/demo/ArticleVariant.js b/components/atom/skeleton/demo/ArticleVariant.js index d25fb1d0ac..2398e456e0 100644 --- a/components/atom/skeleton/demo/ArticleVariant.js +++ b/components/atom/skeleton/demo/ArticleVariant.js @@ -14,6 +14,9 @@ import { import AtomSkeleton, {atomSkeletonVariants} from '../src/index.js' +const height = 200 +const width = 200 + const ArticleVariant = ({className}) => { const [variantState, setVariantState] = useState() return ( @@ -44,7 +47,11 @@ const ArticleVariant = ({className}) => {

- +
) } diff --git a/components/atom/skeleton/package.json b/components/atom/skeleton/package.json index 8193374e78..7ce157fec0 100644 --- a/components/atom/skeleton/package.json +++ b/components/atom/skeleton/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-skeleton", - "version": "1.5.0", + "version": "1.6.0", "description": "Display the loading state of a component while avoding layout shift", "main": "lib/index.js", "scripts": { diff --git a/components/atom/slider/CHANGELOG.md b/components/atom/slider/CHANGELOG.md index e53f1cab9b..858af22095 100644 --- a/components/atom/slider/CHANGELOG.md +++ b/components/atom/slider/CHANGELOG.md @@ -1,5 +1,25 @@ # CHANGELOG +# 1.30.0 (2022-10-01) + + +### Features + +* **components/atom/slider:** Remove -system ([4881c4b](https://github.com/SUI-Components/sui-components/commit/4881c4b79e5f2f4da5e6986c542ac36e253d1316)) + + + +# 1.29.0 (2022-09-30) + + +### Features + +* **components/atom/slider:** add z-index in tooltip ([8eeca33](https://github.com/SUI-Components/sui-components/commit/8eeca333bf1c944de479ed3c3383d5fba03b2dbc)) +* **Root:** Delete undefined dependencies ([c145905](https://github.com/SUI-Components/sui-components/commit/c145905350328925ba6fda2a462d7f8b508c8ea0)) +* **Root:** Merge commit ([d3735d0](https://github.com/SUI-Components/sui-components/commit/d3735d0644332e674d5a5b6291680697f0d6f7c4)) + + + # 1.28.0 (2022-07-06) diff --git a/components/atom/slider/README.md b/components/atom/slider/README.md index 7d2e937fcf..7bb0500d3f 100644 --- a/components/atom/slider/README.md +++ b/components/atom/slider/README.md @@ -20,6 +20,16 @@ import AtomSlider from '@s-ui/react-atom-slider' ``` +### Basic usage with custom markers (only first and last position) + +```js + +``` + ### Step 25 and default value 50 ```js diff --git a/components/atom/slider/package.json b/components/atom/slider/package.json index 3e78add891..b05be9a37f 100644 --- a/components/atom/slider/package.json +++ b/components/atom/slider/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-slider", - "version": "1.28.0", + "version": "1.30.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/atom/slider/src/index.js b/components/atom/slider/src/index.js index ace2d5779a..4eaeebfead 100644 --- a/components/atom/slider/src/index.js +++ b/components/atom/slider/src/index.js @@ -155,7 +155,7 @@ AtomSlider.propTypes = { /* only if range=false, shows a position fixed label with the current value instead of a tooltip */ valueLabel: PropTypes.bool, - /* Set your own mark labels */ + /* Set your own mark labels, usually first and last positions */ marks: PropTypes.array, /* callback to format the value shown as label */ diff --git a/components/atom/slider/src/styles/index.scss b/components/atom/slider/src/styles/index.scss index ce98114ade..654bcec5db 100644 --- a/components/atom/slider/src/styles/index.scss +++ b/components/atom/slider/src/styles/index.scss @@ -147,6 +147,8 @@ $class-tooltip-hidden: '#{$class-tooltip}-hidden'; } #{$class-tooltip} { + z-index: $z-atom-slider-tooltip; + #{$class-tooltip-inner} { background: transparent; border-color: transparent; diff --git a/components/atom/slider/src/styles/settings.scss b/components/atom/slider/src/styles/settings.scss index e49c28b540..c303d303f2 100644 --- a/components/atom/slider/src/styles/settings.scss +++ b/components/atom/slider/src/styles/settings.scss @@ -13,7 +13,7 @@ $ml-atom-slider-handle: 0 !default; $ml-atom-slider-label: 0 !default; $mb-atom-slider-label: $m-m !default; $trf-atom-slider-label: translateX(-50%) !default; -$c-atom-slider-label: $c-system !default; +$c-atom-slider-label: $c-black !default; $w-atom-slider-handle: 24px !default; $b-atom-slider-handle: $bdw-s solid $c-primary !default; $bxsh-atom-slider-handle: none !default; @@ -22,3 +22,5 @@ $h-atom-slider-track: 8px !default; $p-atom-slider: $p-l !default; $mt-atom-slider-mark: 10px !default; + +$z-atom-slider-tooltip: auto !default; diff --git a/components/atom/spinner/CHANGELOG.md b/components/atom/spinner/CHANGELOG.md index 02a9f39827..d11c59e8ba 100644 --- a/components/atom/spinner/CHANGELOG.md +++ b/components/atom/spinner/CHANGELOG.md @@ -1,5 +1,15 @@ # CHANGELOG +# 2.3.0 (2022-10-06) + + +### Features + +* **Root:** Delete undefined dependencies ([c145905](https://github.com/SUI-Components/sui-components/commit/c145905350328925ba6fda2a462d7f8b508c8ea0)) +* **Root:** Merge commit ([d3735d0](https://github.com/SUI-Components/sui-components/commit/d3735d0644332e674d5a5b6291680697f0d6f7c4)) + + + # 2.2.0 (2022-06-20) diff --git a/components/atom/spinner/package.json b/components/atom/spinner/package.json index 1acd22447d..54ffca2ff5 100644 --- a/components/atom/spinner/package.json +++ b/components/atom/spinner/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-spinner", - "version": "2.2.0", + "version": "2.3.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/atom/switch/CHANGELOG.md b/components/atom/switch/CHANGELOG.md index a0e9bdffb2..b63522fba0 100644 --- a/components/atom/switch/CHANGELOG.md +++ b/components/atom/switch/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# 1.28.0 (2022-10-06) + + + # 1.27.0 (2022-06-20) diff --git a/components/atom/switch/package.json b/components/atom/switch/package.json index e8af0fcd64..40fe1944ea 100644 --- a/components/atom/switch/package.json +++ b/components/atom/switch/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-switch", - "version": "1.27.0", + "version": "1.28.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/atom/table/CHANGELOG.md b/components/atom/table/CHANGELOG.md index 77f680d322..cdc85eaff5 100644 --- a/components/atom/table/CHANGELOG.md +++ b/components/atom/table/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# 1.14.0 (2022-10-06) + + + # 1.13.0 (2022-06-20) diff --git a/components/atom/table/package.json b/components/atom/table/package.json index c0e14f8150..fc033aa4ea 100644 --- a/components/atom/table/package.json +++ b/components/atom/table/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-table", - "version": "1.13.0", + "version": "1.14.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/atom/tag/CHANGELOG.md b/components/atom/tag/CHANGELOG.md index d26397c0f4..59795e42a7 100644 --- a/components/atom/tag/CHANGELOG.md +++ b/components/atom/tag/CHANGELOG.md @@ -1,5 +1,14 @@ # CHANGELOG +# 2.44.0 (2022-10-01) + + +### Features + +* **components/atom/tag:** Remove -system ([50ba722](https://github.com/SUI-Components/sui-components/commit/50ba7220a71b7b123bb8e93dd3db5dde3911e5cc)) + + + # 2.43.0 (2022-07-27) diff --git a/components/atom/tag/demo/articles/ArticleDesign.js b/components/atom/tag/demo/articles/ArticleDesign.js index 99ff42eaea..43bf315fb1 100644 --- a/components/atom/tag/demo/articles/ArticleDesign.js +++ b/components/atom/tag/demo/articles/ArticleDesign.js @@ -34,28 +34,32 @@ const ArticleDesign = ({className}) => { )} {Object.values(atomTagDesigns) .reverse() - .map((size, index) => ( + .map((design, index) => ( - + - + - + - + diff --git a/components/atom/tag/package.json b/components/atom/tag/package.json index 87c82d74b3..ac4c7ba2c3 100644 --- a/components/atom/tag/package.json +++ b/components/atom/tag/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-tag", - "version": "2.43.0", + "version": "2.43.0-beta.0", "description": "", "main": "lib/index.js", "scripts": { @@ -9,7 +9,7 @@ "build:styles": "cpx './src/**/*.scss' ./lib" }, "dependencies": { - "@s-ui/component-dependencies": "latest" + "@s-ui/component-dependencies": "1" }, "keywords": [], "author": "", diff --git a/components/atom/tag/src/_settings.scss b/components/atom/tag/src/_settings.scss index 6b0ff182d2..084cf259cc 100644 --- a/components/atom/tag/src/_settings.scss +++ b/components/atom/tag/src/_settings.scss @@ -3,18 +3,42 @@ $bgc-atom-tag: color-variation($c-gray, 3) !default; $mw-label: 240px !default; // sizes +$h-atom-tag-xl: 48px !default; $h-atom-tag-l: 40px !default; $h-atom-tag-m: 32px !default; $h-atom-tag-s: 24px !default; +$h-atom-tag-xs: 16px !default; $m-atom-tag: $m-m !default; +$p-atom-tag-xl: 0 $p-l !default; $p-atom-tag-l: 0 $p-l !default; $p-atom-tag-m: 0 $p-l !default; $p-atom-tag-s: 0 $p-m !default; +$p-atom-tag-xs: 0 $p-m !default; -$p-atom-tag-hasIcon-hasClose: ( +$atom-tag-sizes: (xlarge, large, medium, small, xsmall) !default; + +$h-atom-tags: ( + xlarge: $h-atom-tag-xl, + large: $h-atom-tag-l, + medium: $h-atom-tag-m, + small: $h-atom-tag-s, + xsmall: $h-atom-tag-xs +) !default; + +$p-atom-tags: ( + xlarge: $p-atom-tag-xl, + large: $p-atom-tag-l, + medium: $p-atom-tag-m, small: $p-atom-tag-s, + xsmall: $p-atom-tag-xs +) !default; + +$p-atom-tag-hasIcon-hasClose: ( + xlarge: $p-atom-tag-xl, + large: $p-atom-tag-l, medium: $p-atom-tag-m, - large: $p-atom-tag-l + small: $p-atom-tag-s, + xsmall: $p-atom-tag-xs ) !default; // outline @@ -37,7 +61,7 @@ $w-atom-tag-clickable: 32px !default; // closable $bgc-atom-tag-closable-icon: transparent !default; -$c-atom-tag-closable-icon: $c-system !default; +$c-atom-tag-closable-icon: $c-black !default; $bgc-atom-tag-closable-icon--hover: $c-gray !default; $c-atom-tag-closable-icon--hover: $c-gray-light-3 !default; diff --git a/components/atom/tag/src/constants.js b/components/atom/tag/src/constants.js index b4c93e45dc..325a796c8c 100644 --- a/components/atom/tag/src/constants.js +++ b/components/atom/tag/src/constants.js @@ -11,10 +11,12 @@ export const ACTIONABLE_ONLY_PROPS = [ export const STANDARD_ONLY_PROPS = ['closeIcon', 'onClose'] -export const SIZES = { +export var SIZES = { + XLARGE: 'xlarge', LARGE: 'large', MEDIUM: 'medium', - SMALL: 'small' + SMALL: 'small', + XSMALL: 'xsmall' } export const LINK_TYPES = { diff --git a/components/atom/tag/src/index.js b/components/atom/tag/src/index.js index 6304e2ab7e..6d118db803 100644 --- a/components/atom/tag/src/index.js +++ b/components/atom/tag/src/index.js @@ -11,43 +11,52 @@ import { } from './constants.js' import StandardTag from './Standard.js' -const AtomTag = props => { - const { - design, - href, - icon, - onClick, - responsive, - size, - type, - readOnly, - disabled, - isFitted = false - } = props +const AtomTag = ({ + design, + href, + icon, + iconPlacement = 'left', + onClick, + responsive, + size = SIZES.MEDIUM, + type, + readOnly, + disabled, + isFitted = false, + ...props +}) => { const isActionable = onClick || href - const classNames = cx( - 'sui-AtomTag', - `sui-AtomTag-${size}`, - design && `sui-AtomTag--${design}`, - icon && 'sui-AtomTag-hasIcon', - responsive && 'sui-AtomTag--responsive', - type && `sui-AtomTag--${type}`, - isFitted && 'sui-AtomTag--isFitted' - ) + const [Component, getter] = isActionable + ? [ActionableTag, getActionableProps] + : [StandardTag, getStandardProps] - return isActionable ? ( - - ) : ( - ) } @@ -119,11 +128,6 @@ AtomTag.propTypes = { isFitted: PropTypes.bool } -AtomTag.defaultProps = { - iconPlacement: 'left', - size: SIZES.MEDIUM -} - export default AtomTag export {DESIGNS as atomTagDesigns} export {LINK_TYPES as linkTypes} diff --git a/components/atom/tag/src/styles/index.scss b/components/atom/tag/src/styles/index.scss index 20738051f5..c96f3d7246 100644 --- a/components/atom/tag/src/styles/index.scss +++ b/components/atom/tag/src/styles/index.scss @@ -6,7 +6,6 @@ $base-class: '.sui-AtomTag'; align-content: center; background-color: $bgc-atom-tag; border: $bd-atom-tag; - border-radius: ceil($h-atom-tag-m * 0.5); box-sizing: border-box; cursor: default; display: inline-flex; @@ -103,7 +102,7 @@ $base-class: '.sui-AtomTag'; } } - &#{$self}--outline { + &#{$base-class}--design-outline { border-color: $c-atom-tag-actionable-invert; color: $c-atom-tag-actionable-invert; fill: $c-atom-tag-actionable-invert; @@ -119,50 +118,32 @@ $base-class: '.sui-AtomTag'; } } - &-small { - height: $h-atom-tag-s; - padding: $p-atom-tag-s; - &.sui-AtomTag-hasIcon.sui-AtomTag-hasClose { - padding: map-get($p-atom-tag-hasIcon-hasClose, 'small'); - } - - & .sui-AtomTag-label { - line-height: $h-atom-tag-s; - } - - .sui-AtomTag-closeable { - @include icon-secondary-clickable-area($h-atom-tag-s); - } - - .sui-AtomTag-icon { - margin-left: 0; - } - - .sui-AtomTag-secondary-icon { - margin-right: 0; - } - } + @each $atom-tag-size in $atom-tag-sizes { + &#{$base-class}--size-#{$atom-tag-size} { + height: map-get($h-atom-tags, $atom-tag-size); + padding: map-get($p-atom-tags, $atom-tag-size); + border-radius: ceil(map-get($h-atom-tags, $atom-tag-size) * 0.5); + &.sui-AtomTag-hasIcon.sui-AtomTag-hasClose { + padding: map-get($p-atom-tag-hasIcon-hasClose, $atom-tag-size); + } - &-medium { - &.sui-AtomTag-hasIcon.sui-AtomTag-hasClose { - padding: map-get($p-atom-tag-hasIcon-hasClose, 'medium'); - } - } + & .sui-AtomTag-label { + line-height: map-get($h-atom-tags, $atom-tag-size); + } - &-large { - border-radius: ceil($h-atom-tag-l * 0.5); - height: $h-atom-tag-l; - padding: $p-atom-tag-l; - &.sui-AtomTag-hasIcon.sui-AtomTag-hasClose { - padding: map-get($p-atom-tag-hasIcon-hasClose, 'large'); - } + .sui-AtomTag-closeable { + @include icon-secondary-clickable-area( + map-get($h-atom-tags, $atom-tag-size) + ); + } - & .sui-AtomTag-label { - line-height: $h-atom-tag-l; - } + .sui-AtomTag-icon { + margin-left: 0; + } - .sui-AtomTag-closeable { - @include icon-secondary-clickable-area($h-atom-tag-l); + .sui-AtomTag-secondary-icon { + margin-right: 0; + } } } @@ -182,22 +163,22 @@ $base-class: '.sui-AtomTag'; } } - &--outline { + &--design-outline { background-color: $bgc-atom-tag-outline; border: $bdw-atom-tag-outline solid $bc-atom-tag-outline; } - @each $name, $type in $atom-tag-types { - $bgc: map-get($type, bgc); - $c: map-get($type, c); - $bgc-hover: map-get($type, bgc-hover); - $c-hover: map-get($type, c-hover); - $bdc: map-get($type, bdc); - $bds: map-get($type, bds); - $bdw: map-get($type, bdw); - $bdc-hover: map-get($type, bdc-hover); - - &--#{$name} { + @each $type-name, $type-value in $atom-tag-types { + $bgc: map-get($type-value, bgc); + $c: map-get($type-value, c); + $bgc-hover: map-get($type-value, bgc-hover); + $c-hover: map-get($type-value, c-hover); + $bdc: map-get($type-value, bdc); + $bds: map-get($type-value, bds); + $bdw: map-get($type-value, bdw); + $bdc-hover: map-get($type-value, bdc-hover); + + &--type-#{$type-name} { background-color: $bgc; border-color: $bdc; border-style: $bds; @@ -212,7 +193,7 @@ $base-class: '.sui-AtomTag'; fill: $c; } - &#{$self}--outline { + &#{$self}--design-outline { border-color: $bgc; color: $bgc; background-color: $c; diff --git a/components/atom/tag/test/index.test.js b/components/atom/tag/test/index.test.js index d772bf9e56..ac1b5c9ea1 100644 --- a/components/atom/tag/test/index.test.js +++ b/components/atom/tag/test/index.test.js @@ -201,14 +201,16 @@ describe(json.name, () => { // Given const library = pkg const expected = { + XLARGE: 'xlarge', LARGE: 'large', MEDIUM: 'medium', - SMALL: 'small' + SMALL: 'small', + XSMALL: 'xsmall' } // When const {atomTagSizes: actual} = library - const {LARGE, MEDIUM, SMALL, ...others} = actual + const {XLARGE, LARGE, MEDIUM, SMALL, XSMALL, ...others} = actual // Then expect(Object.keys(others).length).to.equal(0) diff --git a/components/atom/textarea/CHANGELOG.md b/components/atom/textarea/CHANGELOG.md index fa3daf55f5..1412cdfef0 100644 --- a/components/atom/textarea/CHANGELOG.md +++ b/components/atom/textarea/CHANGELOG.md @@ -1,5 +1,15 @@ # CHANGELOG +# 2.20.0 (2022-10-06) + + +### Features + +* **Root:** Delete undefined dependencies ([c145905](https://github.com/SUI-Components/sui-components/commit/c145905350328925ba6fda2a462d7f8b508c8ea0)) +* **Root:** Merge commit ([d3735d0](https://github.com/SUI-Components/sui-components/commit/d3735d0644332e674d5a5b6291680697f0d6f7c4)) + + + # 2.19.0 (2022-06-20) diff --git a/components/atom/textarea/package.json b/components/atom/textarea/package.json index 030c8143f7..b26903a7ff 100644 --- a/components/atom/textarea/package.json +++ b/components/atom/textarea/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-textarea", - "version": "2.19.0", + "version": "2.20.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/atom/tooltip/CHANGELOG.md b/components/atom/tooltip/CHANGELOG.md index 1cffe00e61..e838575023 100644 --- a/components/atom/tooltip/CHANGELOG.md +++ b/components/atom/tooltip/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# 2.5.0 (2022-10-06) + + + # 2.4.0 (2022-06-20) diff --git a/components/atom/tooltip/package.json b/components/atom/tooltip/package.json index a90bd6612c..ffbf00afda 100644 --- a/components/atom/tooltip/package.json +++ b/components/atom/tooltip/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-tooltip", - "version": "2.4.0", + "version": "2.5.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/atom/upload/CHANGELOG.md b/components/atom/upload/CHANGELOG.md index 954d5e3fd2..4d638f172e 100644 --- a/components/atom/upload/CHANGELOG.md +++ b/components/atom/upload/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# 3.10.0 (2022-10-06) + + + # 3.9.0 (2022-06-20) diff --git a/components/atom/upload/package.json b/components/atom/upload/package.json index 47b4097f76..95a208d30a 100644 --- a/components/atom/upload/package.json +++ b/components/atom/upload/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-upload", - "version": "3.9.0", + "version": "3.10.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/atom/validationText/CHANGELOG.md b/components/atom/validationText/CHANGELOG.md index 1b8d657138..5d7afeed95 100644 --- a/components/atom/validationText/CHANGELOG.md +++ b/components/atom/validationText/CHANGELOG.md @@ -1,5 +1,26 @@ # CHANGELOG +# 1.9.0 (2022-10-06) + + +### Features + +* **components/atom/helpText:** admit react nodes as a text ([f41967e](https://github.com/SUI-Components/sui-components/commit/f41967ec354662dcc551562ccfe75aa729aff29e)) +* **components/atom/validationText:** admit react nodes as a text ([ba8379b](https://github.com/SUI-Components/sui-components/commit/ba8379babb0085e61f99de0e69740f749e558b88)) + + + +# 1.8.0 (2022-09-20) + + +### Features + +* **components/atom/validationText:** add margin top variable ([38cfc80](https://github.com/SUI-Components/sui-components/commit/38cfc8043787aaf23bf8a96cbbf97cdaed1ceeeb)) +* **components/atom/validationText:** update margin ([7a2e11e](https://github.com/SUI-Components/sui-components/commit/7a2e11ebac2eaf128eb06b0e9cde21e76a42f8ae)) +* **components/atom/validationText:** update margin variable ([1df0b7d](https://github.com/SUI-Components/sui-components/commit/1df0b7db3ae148d9f26a9ada6a71d6ea317ac72c)) + + + # 1.7.0 (2022-06-20) diff --git a/components/atom/validationText/demo/articles/ArticleDefault.js b/components/atom/validationText/demo/articles/ArticleDefault.js new file mode 100644 index 0000000000..59d325f576 --- /dev/null +++ b/components/atom/validationText/demo/articles/ArticleDefault.js @@ -0,0 +1,69 @@ +import {useState} from 'react' + +import PropTypes from 'prop-types' + +import { + Article, + Cell, + Code, + Grid, + H2, + Input, + Label, + Paragraph, + RadioButton +} from '@s-ui/documentation-library' + +import AtomValidationText, {AtomValidationTextTypes} from '../../src/index.js' +import {flexCenteredStyle} from '../settings.js' + +const ArticleDefault = ({className}) => { + const [mode, setMode] = useState('light') + return ( +
+

Type

+ + using the prop type user can inherit the helpText color + styler for validation text usages. + + setMode(mode === 'light' ? 'dark' : 'light')} + label={mode} + /> +
+
+ + {Object.values(AtomValidationTextTypes).map((type, index) => ( + + + + ))} + {Object.values(AtomValidationTextTypes).map((type, index) => ( + + + + + ))} + +
+ ) +} + +ArticleDefault.propTypes = { + className: PropTypes.string +} + +export default ArticleDefault diff --git a/components/atom/validationText/demo/articles/ArticleNodes.js b/components/atom/validationText/demo/articles/ArticleNodes.js new file mode 100644 index 0000000000..29a333dd28 --- /dev/null +++ b/components/atom/validationText/demo/articles/ArticleNodes.js @@ -0,0 +1,59 @@ +import PropTypes from 'prop-types' + +import { + Article, + Cell, + Grid, + H2, + Input, + Label, + Paragraph +} from '@s-ui/documentation-library' + +import AtomValidationText, {AtomValidationTextTypes} from '../../src/index.js' +import {flexCenteredStyle, nodeText} from '../settings.js' + +const ArticleNodes = ({className}) => { + return ( +
+

Node texts

+ + The component is prepared for admit react node elements as a text. + +
+
+ + {Object.values(AtomValidationTextTypes).map((type, index) => ( + + + + ))} + {Object.values(AtomValidationTextTypes).map((type, index) => ( + + + + + ))} + +
+ ) +} + +ArticleNodes.propTypes = { + className: PropTypes.string +} + +export default ArticleNodes diff --git a/components/atom/validationText/demo/index.js b/components/atom/validationText/demo/index.js index 5c5d89f089..a48da57383 100644 --- a/components/atom/validationText/demo/index.js +++ b/components/atom/validationText/demo/index.js @@ -1,23 +1,10 @@ -import {useState} from 'react' +import {H1, Paragraph} from '@s-ui/documentation-library' -import { - Article, - Cell, - Code, - Grid, - H1, - H2, - Input, - Label, - Paragraph, - RadioButton -} from '@s-ui/documentation-library' - -import AtomValidationText, {AtomValidationTextTypes} from '../src/index.js' -import {flexCenteredStyle} from './settings.js' +import ArticleDefault from './articles/ArticleDefault.js' +import ArticleNodes from './articles/ArticleNodes.js' +import {CLASS_SECTION} from './settings.js' export default () => { - const [mode, setMode] = useState('light') return (

Validation Text

@@ -25,45 +12,9 @@ export default () => { Validation text is used for indicating whether the entered data is correct. It is provided by using the "Help Text" plus one color each. -
-

Type

- - using the prop type user can inherit the helpText color - styler for validation text usages. - - setMode(mode === 'light' ? 'dark' : 'light')} - label={mode} - /> -
-
- - {Object.values(AtomValidationTextTypes).map((type, index) => ( - - - - ))} - {Object.values(AtomValidationTextTypes).map((type, index) => ( - - - - - ))} - -
+ +
+
) } diff --git a/components/atom/validationText/demo/package.json b/components/atom/validationText/demo/package.json index 6ab06e87a2..eb54c97a6c 100644 --- a/components/atom/validationText/demo/package.json +++ b/components/atom/validationText/demo/package.json @@ -9,5 +9,8 @@ }, "keywords": [], "author": "", - "license": "ISC" + "license": "ISC", + "dependencies": { + "@s-ui/react-atom-icon": "1" + } } diff --git a/components/atom/validationText/demo/settings.js b/components/atom/validationText/demo/settings.js index 566c484899..09e20c085c 100644 --- a/components/atom/validationText/demo/settings.js +++ b/components/atom/validationText/demo/settings.js @@ -1,3 +1,9 @@ +import {AntDesignIcon} from '@s-ui/documentation-library' +import AtomIcon from '@s-ui/react-atom-icon' + +export const BASE_CLASS_DEMO = `DemoValidationText` +export const CLASS_SECTION = `${BASE_CLASS_DEMO}-section` + export const flexCenteredStyle = { display: 'flex', justifyContent: 'center', @@ -5,3 +11,15 @@ export const flexCenteredStyle = { alignItems: 'center', alignContent: 'center' } + +export const checkIcon = ( + + + +) + +export const nodeText = ( + + node text{checkIcon} + +) diff --git a/components/atom/validationText/package.json b/components/atom/validationText/package.json index 950be64da0..706a87e9dd 100644 --- a/components/atom/validationText/package.json +++ b/components/atom/validationText/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-atom-validation-text", - "version": "1.7.0", + "version": "1.9.0", "description": "", "main": "lib/index.js", "scripts": { @@ -9,7 +9,8 @@ "build:styles": "cpx './src/**/*.scss' ./lib" }, "dependencies": { - "@s-ui/component-dependencies": "1" + "@s-ui/component-dependencies": "1", + "@s-ui/react-primitive-injector": "1" }, "keywords": [], "author": "", diff --git a/components/atom/validationText/src/index.js b/components/atom/validationText/src/index.js index 1934f37fa6..2217690962 100644 --- a/components/atom/validationText/src/index.js +++ b/components/atom/validationText/src/index.js @@ -1,16 +1,30 @@ +import {forwardRef} from 'react' + import PropTypes from 'prop-types' +import Injector from '@s-ui/react-primitive-injector' + import {getClassNames, TYPES} from './settings.js' -const AtomValidationText = function ({type, text}) { - return {text} -} +const AtomValidationText = forwardRef(({type, text}, forwardedRef) => { + const isTextString = typeof text === 'string' + const Component = isTextString ? 'span' : Injector + return ( + + {text} + + ) +}) AtomValidationText.displayName = 'AtomValidationText' AtomValidationText.propTypes = { type: PropTypes.oneOf(Object.values(TYPES)).isRequired, - text: PropTypes.string.isRequired + text: PropTypes.oneOfType([PropTypes.string, PropTypes.node, PropTypes.bool]) + .isRequired } export default AtomValidationText diff --git a/components/atom/validationText/src/styles/index.scss b/components/atom/validationText/src/styles/index.scss index f75156fb5d..1a729fa12e 100644 --- a/components/atom/validationText/src/styles/index.scss +++ b/components/atom/validationText/src/styles/index.scss @@ -3,7 +3,8 @@ $base-class: '.sui-AtomValidationText'; #{$base-class} { display: block; font-size: $fz-atom-validation-text; - margin: $m-s 0 0; + margin: $m-atom-validation-text; + @each $type, $color in $validation { &--#{$type} { color: $color; diff --git a/components/atom/validationText/src/styles/settings.scss b/components/atom/validationText/src/styles/settings.scss index 7513bf9d1d..b05caeb484 100644 --- a/components/atom/validationText/src/styles/settings.scss +++ b/components/atom/validationText/src/styles/settings.scss @@ -1,2 +1,3 @@ $fz-atom-validation-text: $fz-xs !default; +$m-atom-validation-text: $m-s 0 0 !default; $validation: success $c-success, error $c-error, alert $c-alert; diff --git a/components/behavior/sticky/CHANGELOG.md b/components/behavior/sticky/CHANGELOG.md index e9ebbd7162..e6bfb59e20 100644 --- a/components/behavior/sticky/CHANGELOG.md +++ b/components/behavior/sticky/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# 1.9.0 (2022-10-06) + + + # 1.8.0 (2022-06-22) diff --git a/components/behavior/sticky/package.json b/components/behavior/sticky/package.json index fc28eccf7e..ba7c136438 100644 --- a/components/behavior/sticky/package.json +++ b/components/behavior/sticky/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-behavior-sticky", - "version": "1.8.0", + "version": "1.9.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/hook/usePortal/.gitignore b/components/hook/usePortal/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/components/hook/usePortal/.npmignore b/components/hook/usePortal/.npmignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/components/hook/usePortal/CHANGELOG.md b/components/hook/usePortal/CHANGELOG.md new file mode 100644 index 0000000000..f2357b83a3 --- /dev/null +++ b/components/hook/usePortal/CHANGELOG.md @@ -0,0 +1,11 @@ +# CHANGELOG + +# 1.1.0 (2022-10-03) + + +### Features + +* **components/hook/usePortal:** first version ([3462540](https://github.com/SUI-Components/sui-components/commit/34625400523cda7345e7d4d99666efa0f4bbd32b)) + + + diff --git a/components/hook/usePortal/README.md b/components/hook/usePortal/README.md new file mode 100644 index 0000000000..51d9faada5 --- /dev/null +++ b/components/hook/usePortal/README.md @@ -0,0 +1,98 @@ +# usePortal + +> Provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component. ([doc](https://reactjs.org/docs/portals.html)) + +## Installation + +```sh +$ npm install @s-ui/react-hook-use-portal +``` + +## Usage + +### Basic usage + +```js +// Array destructuring +const Foo = props => { + const [Portal] = usePortal() + return ( + <> + This content is rendered on the propper VDOM tree. + This content added at the end of the document.body + + ) +} +``` + +```js +// Object destructuring +const Foo = props => { + const {Portal} = usePortal() + return ( + <> + This content is rendered on the propper VDOM tree. + This content added at the end of the document.body + + ) +} +``` + +### Target Configuration + +Target is the DOM place where the Portal might the appended. By default it is document.body. To set a different element, just add it to the hook configuration option arguments object: + +```js +const Foo = props => { + const portalContainer = useRef() + const {Portal} = usePortal({target: portalContainer.current}) + return ( + <> +
+ This content is rendered on the propper VDOM tree. + + This content added at the end of the portalContainer element + +
+
{/** <-- this is the container where the portal children will be rendered **/} + + ) +} +``` + +### Open and Close + +Portals can be opened and closed. + +```js +// Statefull (uncontrolled) +const Foo = props => { + const {Portal, open, close, isOpen} = usePortal() + return ( + <> + {!isOpen && } + + This is added at the end of the document.body + + + + ) +} +``` + +```js +// Stateless (controlled) +const Foo = props => { + const [isOpen, setIsOpen] = useState(true) + const {Portal} = usePortal() + return ( + <> + {!isOpen && } + + This is added at the end of the document.body + + + + ) +} +``` \ No newline at end of file diff --git a/components/hook/usePortal/demo/articles/ArticleCloseOnEvent.js b/components/hook/usePortal/demo/articles/ArticleCloseOnEvent.js new file mode 100644 index 0000000000..afdc94f88e --- /dev/null +++ b/components/hook/usePortal/demo/articles/ArticleCloseOnEvent.js @@ -0,0 +1,116 @@ +import {useRef, useState} from 'react' + +import PropTypes from 'prop-types' + +import { + Article, + Box, + Cell, + Code, + Grid, + H2, + H3, + ListItem, + Paragraph, + RadioButton, + UnorderedList +} from '@s-ui/documentation-library' + +import usePortal from '../../src/index.js' + +const ArticleCloseOnEvent = ({className}) => { + // outside click + const targetedOutsideClickRef = useRef() + const [isOpenTargetedOutsideClick, setIsOpenTargetedOutsideClick] = + useState(false) + const {Portal: PortalOutsideClick} = usePortal({ + hasCloseOnOutsideClick: true, + target: targetedOutsideClickRef.current, + isOpen: isOpenTargetedOutsideClick, + onClose: () => { + setIsOpenTargetedOutsideClick(!isOpenTargetedOutsideClick) + } + }) + + // esc press + const targetedEscPressRef = useRef() + const [isOpenCloseOnEsc, setIsOpenCloseOnEsc] = useState(false) + const {Portal: PortalEscPress} = usePortal({ + hasCloseOnEsc: true, + target: targetedEscPressRef.current, + isOpen: isOpenCloseOnEsc, + onClose: () => { + setIsOpenCloseOnEsc(!isOpenCloseOnEsc) + } + }) + return ( +
+

Configured event listeners

+ + usePortal hook has 2 configuration properties for event + triggers: + + + + hasCloseOnOutsideClick (bool|false) + + + hasCloseOnEsc (bool|false) + + +

hasCloseOnOutsideClick

+ + If the hook is configured with this feature enabled (true), the portal + will close on every single clicking event out of its own bounding area. + + + + + setIsOpenTargetedOutsideClick(!isOpenTargetedOutsideClick) + } + /> + + +
+ + + + + This text is closed when clicking outside! + + +

hasCloseOnEsc

+ + If the hook is configured with this feature enabled (true), the portal + will close on every single 'Esc' keypress event. + + + + setIsOpenCloseOnEsc(!isOpenCloseOnEsc)} + /> + + +
+ + + + + This text is closed when pressing Esc key! + + + Portals can use both behaviors enabled also. +
+ ) +} + +ArticleCloseOnEvent.propTypes = { + className: PropTypes.string +} + +export default ArticleCloseOnEvent diff --git a/components/hook/usePortal/demo/articles/ArticleCustom.js b/components/hook/usePortal/demo/articles/ArticleCustom.js new file mode 100644 index 0000000000..c1697c4608 --- /dev/null +++ b/components/hook/usePortal/demo/articles/ArticleCustom.js @@ -0,0 +1,28 @@ +import PropTypes from 'prop-types' + +import {Article, Button, H2, Paragraph} from '@s-ui/documentation-library' + +import useTooltip from './ArticleCustom/useTooltip.js' + +const ArticleCustom = ({className}) => { + const [bind, Tooltip] = useTooltip() + return ( +
+

Custom

+ + You can use the hook defining custom event handlers to extend its + functionalities to may other ways like creating tooltips. + + + + hello world + +
+ ) +} + +ArticleCustom.propTypes = { + className: PropTypes.string +} + +export default ArticleCustom diff --git a/components/hook/usePortal/demo/articles/ArticleCustom/usePopper.js b/components/hook/usePortal/demo/articles/ArticleCustom/usePopper.js new file mode 100644 index 0000000000..bc02ae8511 --- /dev/null +++ b/components/hook/usePortal/demo/articles/ArticleCustom/usePopper.js @@ -0,0 +1,136 @@ +import {useMemo, useRef, useState} from 'react' +import isEqual from 'react-fast-compare' + +import {createPopper as defaultCreatePopper} from '@popperjs/core' + +import useIsomorphicLayoutEffect from '@s-ui/react-hooks/lib/useIsomorphicLayoutEffect' + +const EMPTY_MODIFIERS = [] + +const usePopper = function usePopper( + referenceElement, + popperElement, + options = {} +) { + const prevOptions = useRef(null) + const optionsWithDefaults = { + onFirstUpdate: options.onFirstUpdate, + placement: options.placement || 'bottom', + strategy: options.strategy || 'absolute', + modifiers: options.modifiers || EMPTY_MODIFIERS + } + + const [state, setState] = useState({ + styles: { + popper: { + position: optionsWithDefaults.strategy, + left: '0', + top: '0' + }, + arrow: { + position: 'absolute' + } + }, + attributes: {} + }) + + const updateStateModifier = useMemo(function () { + return { + name: 'updateState', + enabled: true, + phase: 'write', + fn: function fn({state}) { + const elements = Object.keys(state.elements) + setState({ + styles: Object.fromEntries( + elements.map(function (element) { + return [element, state.styles[element] || {}] + }) + ), + attributes: Object.fromEntries( + elements.map(function (element) { + return [element, state.attributes[element]] + }) + ) + }) + }, + requires: ['computeStyles'] + } + }, []) + + const popperOptions = useMemo( + function () { + const newOptions = { + onFirstUpdate: optionsWithDefaults.onFirstUpdate, + placement: optionsWithDefaults.placement, + strategy: optionsWithDefaults.strategy, + modifiers: [].concat(optionsWithDefaults.modifiers, [ + updateStateModifier, + { + name: 'applyStyles', + enabled: false + } + ]) + } + + if (isEqual(prevOptions.current, newOptions)) { + return prevOptions.current || newOptions + } else { + prevOptions.current = newOptions + return newOptions + } + }, + [ + optionsWithDefaults.onFirstUpdate, + optionsWithDefaults.placement, + optionsWithDefaults.strategy, + optionsWithDefaults.modifiers, + updateStateModifier + ] + ) + const popperInstanceRef = useRef() + + useIsomorphicLayoutEffect( + function () { + if (popperInstanceRef.current) { + popperInstanceRef.current.setOptions(popperOptions) + } + }, + [popperOptions] + ) + useIsomorphicLayoutEffect( + function () { + if ( + referenceElement == null || + popperElement == null || + popperInstanceRef.current + ) { + return + } + + const createPopper = options.createPopper || defaultCreatePopper + const popperInstance = createPopper( + referenceElement, + popperElement, + popperOptions + ) + popperInstanceRef.current = popperInstance + return function () { + popperInstance.destroy() + popperInstanceRef.current = null + } + }, + [referenceElement, popperElement, options.createPopper] + ) + return { + state: popperInstanceRef.current ? popperInstanceRef.current.state : null, + styles: state.styles, + attributes: state.attributes, + update: popperInstanceRef.current ? popperInstanceRef.current.update : null, + forceUpdate: popperInstanceRef.current + ? popperInstanceRef.current.forceUpdate + : null + } +} + +export default usePopper diff --git a/components/hook/usePortal/demo/articles/ArticleCustom/useTooltip.js b/components/hook/usePortal/demo/articles/ArticleCustom/useTooltip.js new file mode 100644 index 0000000000..e752f29b0e --- /dev/null +++ b/components/hook/usePortal/demo/articles/ArticleCustom/useTooltip.js @@ -0,0 +1,62 @@ +import {useCallback, useState} from 'react' + +import cx from 'classnames' + +import usePortal from '../../../src/index.js' +import usePopper from './usePopper.js' + +const useTooltip = ({isOpen, ...config} = {}) => { + const [isVisible, setIsVisible] = useState(false) + const { + Portal, + onMouseEnter, + onMouseLeave, + portalRef, + open, + close, + isOpen: isOpened, + triggerRef + } = usePortal({ + onMouseEnter: event => open(event), + onMouseLeave: event => close(event), + onOpen: () => setIsVisible(true), + onClose: () => setIsVisible(false), + isOpen: false, + ...config + }) + + const {styles, attributes} = usePopper( + triggerRef.current, + isVisible ? portalRef.current : null + ) + + const Tooltip = useCallback( + ({children, className, style = {}, ...props}) => { + return ( + + {children} + + ) + }, + [isOpened, styles, attributes] + ) + + return [ + { + ref: triggerRef, + onMouseEnter, + onMouseLeave + }, + Tooltip + ] +} + +export default useTooltip diff --git a/components/hook/usePortal/demo/articles/ArticleDefault.js b/components/hook/usePortal/demo/articles/ArticleDefault.js new file mode 100644 index 0000000000..a8f7e5f004 --- /dev/null +++ b/components/hook/usePortal/demo/articles/ArticleDefault.js @@ -0,0 +1,132 @@ +import {useRef} from 'react' + +import PropTypes from 'prop-types' + +import { + Anchor, + Article, + Bold, + Box, + Button, + Code, + H2, + H3, + H4, + ListItem, + OrderedList, + Paragraph, + UnorderedList +} from '@s-ui/documentation-library' + +import usePortal from '../../src/index.js' + +const ArticleDefault = ({className}) => { + const {Portal, portalRef} = usePortal() + const articleRef = useRef() + return ( +
+

Default

+ + The portal is default appended at the document body. It is default + opened unless you define its initial isOpen configuration + settings to false. + + + + +

Default Portal Result

+ + This text is portaled at the end of document.body from the{' '} + articleRef.current.scrollIntoView()} + style={{ + border: 0, + padding: 0, + margin: 0, + backgroundColor: 'transparent' + }} + > + default demo + + ! + +
+
+ + usePortal hook admits many different options as an{' '} + object argument to configure the portal: + + + + isOpen (boolean|true): sets the initial state visible or + not. + + + onOpen (func): opens the portal + + + onClose (func): closes the portal + + + onToggle (func): changes between open/close the portal + + + hasCloseOnOutsideClick (boolean|false): auto-close the + portal on outside click + + + hasCloseOnEsc (boolean|false): autoClose the portal on + 'Esc' press + + +

Result

+ + usePortal hook result can be destructured using an array or + an object. + +

Array

+ + const [Portal, open, close, isOpen, togglePortal, triggerRef, portalRef] + = usePortal() + +

Object

+ + { + 'const {Portal, open, close, isOpen, togglePortal, triggerRef, portalRef} = usePortal()' + } + + + + Portal: The portal component + + + open: The action to open the portal + + + close: The action to close the portal + + + isOpen: The portal current state + + + togglePortal: The action to toggle the state + + + triggerRef: The trigger element ref + + + portalRef: The portal element ref + + +
+ ) +} + +ArticleDefault.propTypes = { + className: PropTypes.string +} + +export default ArticleDefault diff --git a/components/hook/usePortal/demo/articles/ArticleStateful.js b/components/hook/usePortal/demo/articles/ArticleStateful.js new file mode 100644 index 0000000000..a6b45c15a3 --- /dev/null +++ b/components/hook/usePortal/demo/articles/ArticleStateful.js @@ -0,0 +1,52 @@ +import {useRef, useState} from 'react' + +import PropTypes from 'prop-types' + +import { + Article, + Box, + Button, + Code, + H2, + Paragraph +} from '@s-ui/documentation-library' + +import usePortal from '../../src/index.js' + +const ArticleStateful = ({className}) => { + const targetedRef = useRef() + const [isOpened, setIsOpened] = useState(true) + const openHandler = () => setIsOpened(true) + const closeHandler = () => setIsOpened(false) + const {Portal} = usePortal({ + target: targetedRef.current, + isOpen: isOpened, + onOpen: openHandler, + onClose: closeHandler + }) + return ( +
+

Stateful Control

+ + You can also define the Portal behavior under the isOpen{' '} + (boolean) Portal prop as a controlled component. + +
+ + + + This is the content of the portal stateful opened. + + + + + {!isOpened && } +
+ ) +} + +ArticleStateful.propTypes = { + className: PropTypes.string +} + +export default ArticleStateful diff --git a/components/hook/usePortal/demo/articles/ArticleStateless.js b/components/hook/usePortal/demo/articles/ArticleStateless.js new file mode 100644 index 0000000000..77c632d57a --- /dev/null +++ b/components/hook/usePortal/demo/articles/ArticleStateless.js @@ -0,0 +1,43 @@ +import {useRef} from 'react' + +import PropTypes from 'prop-types' + +import { + Article, + Box, + Button, + Code, + H2, + Paragraph +} from '@s-ui/documentation-library' + +import usePortal from '../../src/index.js' + +const ArticleStateless = ({className}) => { + const targetedRef = useRef() + const {Portal, open, close, isOpen} = usePortal({target: targetedRef.current}) + return ( +
+

Stateless Control

+ + You can use an stateless Portal with its open{' '} + and close{' '} + + + {'const {Portal, open, close, isOpen} = usePortal()'} + +
+ + + + + + {!isOpen && } +
+ ) +} +ArticleStateless.propTypes = { + className: PropTypes.string +} + +export default ArticleStateless diff --git a/components/hook/usePortal/demo/articles/ArticleTarget.js b/components/hook/usePortal/demo/articles/ArticleTarget.js new file mode 100644 index 0000000000..940a5b7393 --- /dev/null +++ b/components/hook/usePortal/demo/articles/ArticleTarget.js @@ -0,0 +1,39 @@ +import {useRef} from 'react' + +import PropTypes from 'prop-types' + +import {Article, Box, Code, H2, Paragraph} from '@s-ui/documentation-library' + +import usePortal from '../../src/index.js' + +const ArticleTarget = ({className}) => { + const targetedRef = useRef() + const {Portal} = usePortal({target: targetedRef.current}) + return ( +
+

Target

+ + Providing a target argument to the hook options will append + the declared Portal's children. + + + {'const { Portal } = usePortal({ target: element })'} + +
+ + + + This text is portaled at the end of the defined target provided in + options! + + + +
+ ) +} + +ArticleTarget.propTypes = { + className: PropTypes.string +} + +export default ArticleTarget diff --git a/components/hook/usePortal/demo/index.js b/components/hook/usePortal/demo/index.js new file mode 100644 index 0000000000..ce56774d3a --- /dev/null +++ b/components/hook/usePortal/demo/index.js @@ -0,0 +1,48 @@ +import { + Anchor, + Bold, + Code, + Emphasis, + H1, + Paragraph +} from '@s-ui/documentation-library' + +import ArticleCloseOnEvent from './articles/ArticleCloseOnEvent.js' +import ArticleCustom from './articles/ArticleCustom.js' +import ArticleDefault from './articles/ArticleDefault.js' +import ArticleStateful from './articles/ArticleStateful.js' +import ArticleStateless from './articles/ArticleStateless.js' +import ArticleTarget from './articles/ArticleTarget.js' +import {CLASS_SECTION} from './settings.js' + +const Demo = () => { + return ( +
+

usePortal

+ + The usePortal hook offers the possibility to + create a{' '} + Portal set + where is defined (default: document.body). + + + * Portals will be displayed in a dark Box to identify them. + +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ ) +} + +export default Demo diff --git a/components/hook/usePortal/demo/index.scss b/components/hook/usePortal/demo/index.scss new file mode 100644 index 0000000000..0164645161 --- /dev/null +++ b/components/hook/usePortal/demo/index.scss @@ -0,0 +1,10 @@ +@import '../src/index'; + +.sui-DemoTooltip { + background-color: $c-primary-light; + border-radius: $bdrs-base; + padding: $p-base; + border-width: $bdw-m; + border-color: $c-primary; + border-style: solid; +} diff --git a/components/hook/usePortal/demo/package.json b/components/hook/usePortal/demo/package.json new file mode 100644 index 0000000000..af4067ae9c --- /dev/null +++ b/components/hook/usePortal/demo/package.json @@ -0,0 +1,17 @@ +{ + "name": "@s-ui/react-hook-use-portal-demo", + "private": true, + "version": "1.0.0", + "description": "", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "@popperjs/core": "2.11.6", + "@s-ui/react-hooks": "1", + "react-fast-compare": "3.2.0" + }, + "keywords": [], + "author": "", + "license": "MIT" +} diff --git a/components/hook/usePortal/demo/settings.js b/components/hook/usePortal/demo/settings.js new file mode 100644 index 0000000000..633d43cc40 --- /dev/null +++ b/components/hook/usePortal/demo/settings.js @@ -0,0 +1,2 @@ +export const BASE_CLASS_DEMO = `DemoUsePortal` +export const CLASS_SECTION = `${BASE_CLASS_DEMO}-section` diff --git a/components/hook/usePortal/package.json b/components/hook/usePortal/package.json new file mode 100644 index 0000000000..3355d1a14d --- /dev/null +++ b/components/hook/usePortal/package.json @@ -0,0 +1,23 @@ +{ + "name": "@s-ui/react-hook-use-portal", + "version": "1.1.0", + "description": "", + "main": "lib/index.js", + "scripts": { + "prepublishOnly": "rimraf ./lib && npm run build:js && npm run build:styles", + "build:js": "babel --presets sui ./src --out-dir ./lib", + "build:styles": "cpx './src/**/*.scss' ./lib" + }, + "dependencies": { + "use-ssr": "1.0.24", + "classnames": "2.2.5", + "@s-ui/react-hooks": "1" + }, + "keywords": [ + "react", + "hook", + "portal" + ], + "author": "", + "license": "MIT" +} diff --git a/components/hook/usePortal/src/index.js b/components/hook/usePortal/src/index.js new file mode 100644 index 0000000000..c88893c9d4 --- /dev/null +++ b/components/hook/usePortal/src/index.js @@ -0,0 +1,235 @@ +/* eslint-disable react/prop-types */ +import { + forwardRef, + useCallback, + useEffect, + useMemo, + useRef, + useState +} from 'react' +import {createPortal, findDOMNode} from 'react-dom' +import {isFragment} from 'react-is' + +import cx from 'classnames' +import PropTypes from 'prop-types' +import useSSR from 'use-ssr' + +import useMergeRefs from '@s-ui/react-hooks/lib/useMergeRefs' + +import {BASE_CLASS, DEFAULT_IS_OPEN} from './settings.js' + +const usePortal = ({ + isOpen: defaultIsOpen = DEFAULT_IS_OPEN, + target = document.body, + onOpen, + onClose, + onToggle, + onClick, + hasCloseOnOutsideClick = false, + hasCloseOnEsc = false, + ...eventHandlers +} = {}) => { + const {isServer} = useSSR() + const [isOpened, setIsOpened] = useState(defaultIsOpen) + + const triggerElement = useRef() // this is the element you are clicking/hovering/whatever, to trigger opening the portal + const portal = useRef() + + const elToMountTo = useMemo(() => { + if (isServer) return + return (target && findDOMNode(target)) || document.body + }, [isServer, target]) + + const createCustomEvent = event => { + if (!event) return {portal, triggerElement, event} + const response = event || {} + if (response.persist) response.persist() + response.portal = portal + response.triggerElement = triggerElement + response.event = event + return response + } + + // this should handle all eventHandlers like onClick, onMouseOver, etc. passed into the config + const customEventHandlers = Object.entries(eventHandlers).reduce( + (acc, [handlerName, eventHandler]) => { + acc[handlerName] = event => { + if (isServer) return + eventHandler(createCustomEvent(event)) + } + return acc + }, + {} + ) + + const setIsOpen = useCallback( + value => event => { + if (isServer) return + const customEvent = createCustomEvent(event) + if (onOpen && value === true) setTimeout(() => onOpen(customEvent), 0) + if (onClose && value === false) onClose(customEvent) + setIsOpened(value) + }, + [isServer, setIsOpened, onClose] + ) + + const openPortal = setIsOpen(true) + const closePortal = setIsOpen(false) + + const togglePortal = event => + isOpened ? closePortal(event) : openPortal(event) + + const handleKeydown = useCallback( + event => + event.key === 'Escape' && hasCloseOnEsc ? closePortal(event) : undefined, + [hasCloseOnEsc, closePortal] + ) + + const handleOutsideMouseClick = useCallback( + event => { + const containsTarget = target => target.current?.contains(event.target) + if ( + containsTarget(portal) || + event.button !== 0 || + !isOpened || + containsTarget(triggerElement) + ) { + return + } + if (hasCloseOnOutsideClick) { + closePortal(event) + } + }, + [isServer, closePortal, hasCloseOnOutsideClick, portal] + ) + + const handleMouseDown = useCallback( + event => { + if (isServer || !(portal.current instanceof HTMLElement)) return + const customEvent = createCustomEvent(event) + if (portal.current.contains(customEvent.target) && onClick) + onClick(customEvent) + handleOutsideMouseClick(event) + }, + [handleOutsideMouseClick] + ) + + // used to remove the event listeners on unmount + const eventListeners = useRef({}) + + useEffect(() => { + if (isServer) return + if ( + !(elToMountTo instanceof HTMLElement) || + !(portal.current instanceof HTMLElement) + ) { + return + } + + // TODO: eventually will need to figure out a better solution for this. + // Surely we can find a way to map onScroll/onWheel -> scroll/wheel better, + // but for all other event handlers. For now this works. + const eventHandlerMap = { + onScroll: 'scroll', + onWheel: 'wheel' + } + Object.entries(eventHandlerMap).forEach( + ([handlerName /* onScroll */, eventListenerName /* scroll */]) => { + if (!eventHandlers[handlerName]) return + eventListeners.current[handlerName] = event => + eventHandlers[handlerName](createCustomEvent(event)) + document.addEventListener( + eventListenerName, + eventListeners.current[handlerName] + ) + } + ) + document.addEventListener('keydown', handleKeydown) + document.addEventListener('mousedown', handleMouseDown) + + return () => { + // handles all special case handlers. Currently only onScroll and onWheel + Object.entries(eventHandlerMap).forEach( + ([handlerName, eventListenerName]) => { + if (!eventHandlers[handlerName]) return + document.removeEventListener( + eventListenerName, + eventListeners.current[handlerName] + ) + delete eventListeners.current[handlerName] + } + ) + document.removeEventListener('keydown', handleKeydown) + document.removeEventListener('mousedown', handleMouseDown) + } + }, [isServer, handleOutsideMouseClick, handleKeydown, elToMountTo, portal]) + + const Portal = useCallback( + forwardRef( + ( + {as: As = 'div', children, isOpen: isOpenProp, className, ...props}, + forwardedRef + ) => { + const ref = useMergeRefs(forwardedRef, portal) + useEffect(() => { + if (isServer) return + if (isOpenProp !== undefined) { + setIsOpened(isOpenProp) + } + }, [isOpenProp]) + + return isOpened + ? createPortal( + ) && { + ref, + className: cx(BASE_CLASS, className), + ...props + })} + > + {children} + , + target + ) + : null + } + ), + [isOpened, portal, target] + ) + Portal.propTypes = { + as: PropTypes.elementType, + children: PropTypes.node, + isOpen: PropTypes.bool, + className: PropTypes.string + } + Portal.displayName = 'Portal' + + return Object.assign( + [ + Portal, + openPortal, + closePortal, + isOpened, + togglePortal, + triggerElement, + portal + ], + { + isOpen: isOpened, + triggerRef: triggerElement, + open: openPortal, + close: closePortal, + toggle: togglePortal, + Portal, + portalRef: portal, + ...customEventHandlers, + bind: { + // used if you want to spread all html attributes onto the target element + ref: triggerElement, + ...customEventHandlers + } + } + ) +} + +export default usePortal diff --git a/components/hook/usePortal/src/index.scss b/components/hook/usePortal/src/index.scss new file mode 100644 index 0000000000..bd7f4f88aa --- /dev/null +++ b/components/hook/usePortal/src/index.scss @@ -0,0 +1 @@ +@import '~@s-ui/theme/lib/index'; diff --git a/components/hook/usePortal/src/settings.js b/components/hook/usePortal/src/settings.js new file mode 100644 index 0000000000..c0afc7796c --- /dev/null +++ b/components/hook/usePortal/src/settings.js @@ -0,0 +1,3 @@ +export const BASE_CLASS = 'sui-Portal' + +export const DEFAULT_IS_OPEN = true diff --git a/components/hook/usePortal/test/index.test.js b/components/hook/usePortal/test/index.test.js new file mode 100644 index 0000000000..b5ea19f213 --- /dev/null +++ b/components/hook/usePortal/test/index.test.js @@ -0,0 +1,254 @@ +/* + * Remember: YOUR COMPONENT IS DEFINED GLOBALLY + * */ + +/* eslint react/jsx-no-undef:0 */ +/* eslint no-undef:0 */ + +import React, {useRef} from 'react' +import ReactDOM from 'react-dom' + +import {/** chai, **/ expect} from 'chai' +import PropTypes from 'prop-types' + +// import chaiDOM from 'chai-dom' +// import sinon from 'sinon' +// import {fireEvent} from '@testing-library/react' +import json from '../package.json' +import * as pkg from '../src/index.js' + +describe(json.name, () => { + const {default: usePortal} = pkg + const Component = ({children, isOpen: isOpenProps, ...handlers}) => { + const targetRef = useRef() + const {Portal, isOpen, open, close, toggle, bind} = usePortal({ + target: targetRef.current, + isOpen: isOpenProps, + ...handlers + }) + + return ( +
+
+ {children} +
+
+ + + + +
+ ) + } + + Component.propTypes = { + children: PropTypes.node, + isOpen: PropTypes.bool + } + + const setup = setupEnvironment(Component) + + it('library should include defined exported elements', () => { + // Given + const library = pkg + const libraryExportedMembers = ['default'] + + // When + const {default: usePortal, ...others} = library + + // Then + expect(Object.keys(library).length).to.equal(libraryExportedMembers.length) + expect(Object.keys(library)).to.have.members(libraryExportedMembers) + expect(Object.keys(others).length).to.equal(0) + }) + + describe('hook', () => { + it('should render without crashing', () => { + // Given + const props = {as: 'h1'} + + // When + const component = + + // Then + const div = document.createElement('div') + ReactDOM.render(component, div) + ReactDOM.unmountComponentAtNode(div) + }) + + it('should first render null', () => { + // Given + const props = {} + + // When + const {container, getByTestId} = setup(props) + const portalContainerElement = getByTestId('portal-test-container') + const portalContainerOriginElement = getByTestId( + 'portal-test-container-origin' + ) + const portalContainerTargetElement = getByTestId( + 'portal-test-container-target' + ) + + // Then + expect(container.innerHTML).to.be.a('string') + expect(container.innerHTML).to.not.have.lengthOf(0) + + expect(portalContainerElement.innerHTML).to.be.a('string') + expect(portalContainerElement.innerHTML).to.not.have.lengthOf(0) + + expect(portalContainerOriginElement.innerHTML).to.be.a('string') + expect(portalContainerOriginElement.innerHTML).to.have.lengthOf(0) + + expect(portalContainerTargetElement.innerHTML).to.be.a('string') + expect(portalContainerTargetElement.innerHTML).to.have.lengthOf(0) + }) + + it('should add portal to the right place', () => { + // Given + const props = {children: 'portal-content'} + + // When + const {container, rerender, getByTestId} = setup(props) + const portalContainerElement = getByTestId('portal-test-container') + const portalContainerOriginElement = getByTestId( + 'portal-test-container-origin' + ) + const portalContainerTargetElement = getByTestId( + 'portal-test-container-target' + ) + + // Then + expect(container.innerHTML).to.be.a('string') + expect(container.innerHTML).to.not.have.lengthOf(0) + + expect(portalContainerElement.innerHTML).to.be.a('string') + expect(portalContainerElement.innerHTML).to.not.have.lengthOf(0) + + expect(portalContainerOriginElement.innerHTML).to.be.a('string') + expect(portalContainerOriginElement.innerHTML).to.have.lengthOf(0) + + expect(portalContainerTargetElement.innerHTML).to.be.a('string') + expect(portalContainerTargetElement.innerHTML).to.have.lengthOf(0) + + // When + rerender() + expect(portalContainerElement.innerHTML).to.be.a('string') + expect(portalContainerElement.innerHTML).to.not.have.lengthOf(0) + + expect(portalContainerOriginElement.innerHTML).to.be.a('string') + expect(portalContainerOriginElement.innerHTML).to.have.lengthOf(0) + + expect(portalContainerTargetElement.innerHTML).to.be.a('string') + expect(portalContainerTargetElement.innerHTML).to.not.have.lengthOf(0) + }) + + it('given isOpen=false should NOT add portal', () => { + // Given + const props = {children: 'portal-content', isOpen: false} + + // When + const {container, rerender, getByTestId} = setup(props) + const portalContainerElement = getByTestId('portal-test-container') + const portalContainerOriginElement = getByTestId( + 'portal-test-container-origin' + ) + const portalContainerTargetElement = getByTestId( + 'portal-test-container-target' + ) + + // Then + expect(container.innerHTML).to.be.a('string') + expect(container.innerHTML).to.not.have.lengthOf(0) + + expect(portalContainerElement.innerHTML).to.be.a('string') + expect(portalContainerElement.innerHTML).to.not.have.lengthOf(0) + + expect(portalContainerOriginElement.innerHTML).to.be.a('string') + expect(portalContainerOriginElement.innerHTML).to.have.lengthOf(0) + + expect(portalContainerTargetElement.innerHTML).to.be.a('string') + expect(portalContainerTargetElement.innerHTML).to.have.lengthOf(0) + + // When + rerender() + expect(portalContainerElement.innerHTML).to.be.a('string') + expect(portalContainerElement.innerHTML).to.not.have.lengthOf(0) + + expect(portalContainerOriginElement.innerHTML).to.be.a('string') + expect(portalContainerOriginElement.innerHTML).to.have.lengthOf(0) + + expect(portalContainerTargetElement.innerHTML).to.be.a('string') + expect(portalContainerTargetElement.innerHTML).to.have.lengthOf(0) + }) + + it('given isOpen=true should add portal', () => { + // Given + const props = {children: 'portal-content', isOpen: true} + + // When + const {container, rerender, getByTestId} = setup(props) + const portalContainerElement = getByTestId('portal-test-container') + const portalContainerOriginElement = getByTestId( + 'portal-test-container-origin' + ) + const portalContainerTargetElement = getByTestId( + 'portal-test-container-target' + ) + + // Then + expect(container.innerHTML).to.be.a('string') + expect(container.innerHTML).to.not.have.lengthOf(0) + + expect(portalContainerElement.innerHTML).to.be.a('string') + expect(portalContainerElement.innerHTML).to.not.have.lengthOf(0) + + expect(portalContainerOriginElement.innerHTML).to.be.a('string') + expect(portalContainerOriginElement.innerHTML).to.have.lengthOf(0) + + expect(portalContainerTargetElement.innerHTML).to.be.a('string') + expect(portalContainerTargetElement.innerHTML).to.have.lengthOf(0) + + // When + rerender() + expect(portalContainerElement.innerHTML).to.be.a('string') + expect(portalContainerElement.innerHTML).to.not.have.lengthOf(0) + + expect(portalContainerOriginElement.innerHTML).to.be.a('string') + expect(portalContainerOriginElement.innerHTML).to.have.lengthOf(0) + + expect(portalContainerTargetElement.innerHTML).to.be.a('string') + expect(portalContainerTargetElement.innerHTML).to.not.have.lengthOf(0) + }) + + describe('fire events', () => { + it.skip('should fire onClose when it is triggered', () => { + // Given + // const spyOnClose = sinon.spy() + // const props = {children: 'portal-content', onClose: spyOnClose} + // When + // const {container, rerender, getByTestId, getByText} = setup(props) + // rerender() + // const portalContainerElement = getByTestId('portal-test-container') + // const portalContainerOriginElement = getByTestId( + // 'portal-test-container-origin' + // ) + // const portalContainerTargetElement = getByTestId( + // 'portal-test-container-target' + // ) + // Then + // expect(portalContainerElement.innerHTML).to.be.a('string') + // expect(portalContainerElement.innerHTML).to.not.have.lengthOf(0) + // + // expect(portalContainerOriginElement.innerHTML).to.be.a('string') + // expect(portalContainerOriginElement.innerHTML).to.have.lengthOf(0) + // + // expect(portalContainerTargetElement.innerHTML).to.be.a('string') + // expect(portalContainerTargetElement.innerHTML).to.not.have.lengthOf(0) + // And + // When + // fireEvent.click(getByText(props.children)) + }) + }) + }) +}) diff --git a/components/layout/grid/CHANGELOG.md b/components/layout/grid/CHANGELOG.md index 7cbd87cb73..425303fd24 100644 --- a/components/layout/grid/CHANGELOG.md +++ b/components/layout/grid/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# 2.13.0 (2022-10-06) + + + # 2.12.0 (2022-06-20) diff --git a/components/layout/grid/package.json b/components/layout/grid/package.json index 711b9d33ae..9ec365d7da 100644 --- a/components/layout/grid/package.json +++ b/components/layout/grid/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-layout-grid", - "version": "2.12.0", + "version": "2.13.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/molecule/accordion/CHANGELOG.md b/components/molecule/accordion/CHANGELOG.md index d886fb855d..84f0106851 100644 --- a/components/molecule/accordion/CHANGELOG.md +++ b/components/molecule/accordion/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# 2.7.0 (2022-10-06) + + + # 2.6.0 (2022-06-20) diff --git a/components/molecule/accordion/package.json b/components/molecule/accordion/package.json index e1ca83b557..ab0aff5b7b 100644 --- a/components/molecule/accordion/package.json +++ b/components/molecule/accordion/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-molecule-accordion", - "version": "2.6.0", + "version": "2.7.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/molecule/accordion/src/styles/index.scss b/components/molecule/accordion/src/styles/index.scss index ee8e226e96..1282c0f8ec 100644 --- a/components/molecule/accordion/src/styles/index.scss +++ b/components/molecule/accordion/src/styles/index.scss @@ -136,7 +136,7 @@ $base-class-item-panel: '#{$base-class-item}Panel'; min-height: $sz-empty-icon; position: relative; overflow: hidden; - transform: rotate(180deg); + transform: rotate(-180deg); &::before, &::after { position: absolute; @@ -148,11 +148,11 @@ $base-class-item-panel: '#{$base-class-item}Panel'; box-sizing: border-box; } &::before { - transform: rotate(-45deg); + transform: rotate(-135deg); left: 33%; } &::after { - transform: rotate(-135deg); + transform: rotate(-45deg); right: 33%; } } diff --git a/components/molecule/autosuggest/CHANGELOG.md b/components/molecule/autosuggest/CHANGELOG.md index 036fc81316..c5ac8b9380 100644 --- a/components/molecule/autosuggest/CHANGELOG.md +++ b/components/molecule/autosuggest/CHANGELOG.md @@ -1,5 +1,14 @@ # CHANGELOG +# 2.66.0 (2022-10-04) + + +### Features + +* **components/molecule/autosuggest:** update the dropdownOption new prop name ([01facc9](https://github.com/SUI-Components/sui-components/commit/01facc9fb7058abda63dc9667fabee5cf44d8d36)) + + + # 2.65.0 (2022-08-22) diff --git a/components/molecule/autosuggest/demo/Icons/index.js b/components/molecule/autosuggest/demo/Icons/index.js index 0bc30a8341..83d665d084 100644 --- a/components/molecule/autosuggest/demo/Icons/index.js +++ b/components/molecule/autosuggest/demo/Icons/index.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types' -import AtomIcon, {atomIconColors, atomIconSizes} from '@s-ui/react-atom-icon' import {AntDesignIcon} from '@s-ui/documentation-library' +import AtomIcon, {atomIconColors, atomIconSizes} from '@s-ui/react-atom-icon' const alert = ( diff --git a/components/molecule/autosuggest/demo/config.js b/components/molecule/autosuggest/demo/config.js index 47b27aa17f..865f65d3cc 100644 --- a/components/molecule/autosuggest/demo/config.js +++ b/components/molecule/autosuggest/demo/config.js @@ -1,6 +1,7 @@ import {withStateValue, withStateValueTags} from '@s-ui/hoc' import MoleculeAutosuggestField from '@s-ui/react-molecule-autosuggest-field' import MoleculeAutosuggestOption from '@s-ui/react-molecule-dropdown-option' + import MoleculeAutosuggest from '../src/index.js' import withDynamicOptions from './hoc/withDynamicOptions.js' import {getAsyncCountriesFromQuery} from './services/index.js' diff --git a/components/molecule/autosuggest/demo/index.js b/components/molecule/autosuggest/demo/index.js index 7da6f5a582..3bf122450b 100644 --- a/components/molecule/autosuggest/demo/index.js +++ b/components/molecule/autosuggest/demo/index.js @@ -9,19 +9,18 @@ import MoleculeAutosuggestOption from '@s-ui/react-molecule-dropdown-option' import ComboCountries from './components/ComboCountries.js' import { - iconClose, - iconSearch, iconAlert, + iconClose, + iconError, iconInfo, - iconError + iconSearch } from './Icons/index.js' - import { CLASS_DEMO_SECTION, - options, MoleculeAutosuggestWithState, MoleculeAutosuggestWithStateTags, - MoleculeAutosuggestWithStateTagsLabels + MoleculeAutosuggestWithStateTagsLabels, + options } from './config.js' import './index.scss' diff --git a/components/molecule/autosuggest/demo/package.json b/components/molecule/autosuggest/demo/package.json index 8e41c8acb2..cf9206a5b1 100644 --- a/components/molecule/autosuggest/demo/package.json +++ b/components/molecule/autosuggest/demo/package.json @@ -15,7 +15,7 @@ "@s-ui/react-atom-button": "1", "@s-ui/react-atom-icon": "1", "@s-ui/react-molecule-autosuggest-field": "2", - "@s-ui/react-molecule-dropdown-option": "1", + "@s-ui/react-molecule-dropdown-option": "2", "@s-ui/react-molecule-input-tags": "2", "@s-ui/react-molecule-select": "1", "axios": "0.21.4" diff --git a/components/molecule/autosuggest/package.json b/components/molecule/autosuggest/package.json index 050c823c40..e74bba9fd6 100644 --- a/components/molecule/autosuggest/package.json +++ b/components/molecule/autosuggest/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-molecule-autosuggest", - "version": "2.65.0", + "version": "2.66.0", "description": "", "main": "lib/index.js", "scripts": { @@ -12,7 +12,7 @@ "@s-ui/js": "2", "@s-ui/react-atom-input": "5", "@s-ui/react-hooks": "1", - "@s-ui/react-molecule-dropdown-list": "1", + "@s-ui/react-molecule-dropdown-list": "2", "@s-ui/react-molecule-input-tags": "2", "@s-ui/react-primitive-injector": "1", "lodash.isequal": "4.5" diff --git a/components/molecule/autosuggest/src/components/InputWithClearUI/index.js b/components/molecule/autosuggest/src/components/InputWithClearUI/index.js index 70c8849133..10ee6c5a90 100644 --- a/components/molecule/autosuggest/src/components/InputWithClearUI/index.js +++ b/components/molecule/autosuggest/src/components/InputWithClearUI/index.js @@ -8,7 +8,7 @@ const InputWithClearUI = ({ onClickClear, isVisibleClear, iconClear, - rightIcon = , + rightIcon, children, ...props }) => { diff --git a/components/molecule/autosuggest/src/index.js b/components/molecule/autosuggest/src/index.js index 083de1d291..f47e016072 100644 --- a/components/molecule/autosuggest/src/index.js +++ b/components/molecule/autosuggest/src/index.js @@ -81,7 +81,7 @@ const MoleculeAutosuggest = ({ refsMoleculeAutosuggestOptions.current[index] = createRef() return cloneElement(child, { innerRef: refsMoleculeAutosuggestOptions.current[index], - onSelectKey: keysSelection + selectKey: keysSelection }) }) @@ -117,7 +117,6 @@ const MoleculeAutosuggest = ({ ev.persist() const {current: domInnerInput} = refMoleculeAutosuggestInput const {current: optionsFromRef} = refsMoleculeAutosuggestOptions - const {current: innerRefInput} = innerRefMoleculeAutosuggestInput const {key} = ev const options = optionsFromRef.map(getTarget) @@ -137,7 +136,7 @@ const MoleculeAutosuggest = ({ else if (isSomeOptionFocused) handleFocusIn(ev) if (key === 'Enter') { typeof onEnter === 'function' && onEnter(ev) - innerRefInput && (!isControlled || autoClose) && innerRefInput.focus() + handleFocusInput() } } } @@ -180,8 +179,22 @@ const MoleculeAutosuggest = ({ } const handleClick = () => { + handleFocusInput() + } + + const handleFocusInput = () => { const {current: innerRefInput} = innerRefMoleculeAutosuggestInput - innerRefInput && (!isControlled || autoClose) && innerRefInput.focus() + if (innerRefInput && (!isControlled || autoClose)) { + innerRefInput.focus() + } + } + + const handleClear = ev => { + ev.stopPropagation() + handleFocusInput() + if (onClear) { + onClear(ev) + } } const autosuggestSelectionProps = { @@ -196,7 +209,7 @@ const MoleculeAutosuggest = ({ keysSelection, onBlur, onChange, - onClear, + onClear: handleClear, onEnter, onFocus, onInputKeyDown: handleInputKeyDown, diff --git a/components/molecule/autosuggestField/CHANGELOG.md b/components/molecule/autosuggestField/CHANGELOG.md index 90b5ff5ce1..5d5bde86d2 100644 --- a/components/molecule/autosuggestField/CHANGELOG.md +++ b/components/molecule/autosuggestField/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# 2.11.0 (2022-10-06) + + + # 2.10.0 (2022-06-22) diff --git a/components/molecule/autosuggestField/demo/package.json b/components/molecule/autosuggestField/demo/package.json index 8d1ed9e8ca..9bb12b8aec 100644 --- a/components/molecule/autosuggestField/demo/package.json +++ b/components/molecule/autosuggestField/demo/package.json @@ -13,7 +13,7 @@ "dependencies": { "@s-ui/hoc": "1", "@s-ui/react-molecule-autosuggest": "1", - "@s-ui/react-molecule-dropdown-option": "1", + "@s-ui/react-molecule-dropdown-option": "2", "@s-ui/react-molecule-field": "1" } } diff --git a/components/molecule/autosuggestField/package.json b/components/molecule/autosuggestField/package.json index 268957c004..8fa02421af 100644 --- a/components/molecule/autosuggestField/package.json +++ b/components/molecule/autosuggestField/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-molecule-autosuggest-field", - "version": "2.10.0", + "version": "2.11.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/molecule/avatar/CHANGELOG.md b/components/molecule/avatar/CHANGELOG.md index 6b5ced7b33..731142dab3 100644 --- a/components/molecule/avatar/CHANGELOG.md +++ b/components/molecule/avatar/CHANGELOG.md @@ -1,5 +1,27 @@ # CHANGELOG +# 1.16.0 (2022-10-06) + + + +# 1.15.0 (2022-10-03) + + +### Features + +* **components/molecule/avatar:** Add primitive injector dependency ([014c3c1](https://github.com/SUI-Components/sui-components/commit/014c3c19eb8e5f4039c02e7180ed4b6459f9d2d9)) + + + +# 1.14.0 (2022-09-29) + + +### Features + +* **components/molecule/avatar:** handle background color for avatar name fallback ([d25b405](https://github.com/SUI-Components/sui-components/commit/d25b4059dadc602e6bcc97649c551bb91d8e847a)) + + + # 1.13.0 (2022-08-12) diff --git a/components/molecule/avatar/demo/ArticleImage.js b/components/molecule/avatar/demo/ArticleImage.js index cc0e1a7824..2592fc9ed0 100644 --- a/components/molecule/avatar/demo/ArticleImage.js +++ b/components/molecule/avatar/demo/ArticleImage.js @@ -20,6 +20,8 @@ const ArticleImage = ({className}) => { const [src, setSrc] = useState( 'https://randomuser.me/api/portraits/men/1.jpg' ) + const [brokenSrc, setBrokenSrc] = useState('https://brokenimagesrc') + const [name, setName] = useState('John Doe') const [fallbackIcon, setFallbackIcon] = useState() return (
@@ -98,6 +100,46 @@ const ArticleImage = ({className}) => { ) : undefined } /> +

Fallback Name

+ + If the provided image fails on loading and the name{' '} + property is passed, the avatar will display the name initials with a + colored background. + + { + setBrokenSrc(event.target.value) + }} + placeholder="image" + fullWidth + /> +
+
+ { + setName(event.target.value) + }} + placeholder="image" + fullWidth + /> +
+
+ + + + ) : undefined + } + />
) } diff --git a/components/molecule/avatar/package.json b/components/molecule/avatar/package.json index 0fc0bbeea3..8348db7971 100644 --- a/components/molecule/avatar/package.json +++ b/components/molecule/avatar/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-molecule-avatar", - "version": "1.13.0", + "version": "1.16.0", "description": "", "main": "lib/index.js", "scripts": { @@ -10,7 +10,8 @@ }, "dependencies": { "@s-ui/react-atom-image": "2", - "@s-ui/react-atom-skeleton": "1" + "@s-ui/react-atom-skeleton": "1", + "@s-ui/react-primitive-injector": "1" }, "peerDependencies": { "@s-ui/component-dependencies": "1" diff --git a/components/molecule/avatar/src/AvatarFallback/index.js b/components/molecule/avatar/src/AvatarFallback/index.js index 61b34f915a..aabcf79d03 100644 --- a/components/molecule/avatar/src/AvatarFallback/index.js +++ b/components/molecule/avatar/src/AvatarFallback/index.js @@ -1,8 +1,8 @@ -import {cloneElement} from 'react' - import cx from 'classnames' import PropTypes from 'prop-types' +import Injector from '@s-ui/react-primitive-injector' + import AvatarFallbackIcon from '../AvatarFallbackIcon/index.js' import {BASE_CLASS_NAME as FALLBACK_ICON_CLASS_NAME} from '../AvatarFallbackIcon/settings.js' import AvatarFallbackName from '../AvatarFallbackName/index.js' @@ -14,11 +14,15 @@ const MoleculeAvatarFallback = ({ ...others }) => { const className = cx(classNameProp, FALLBACK_ICON_CLASS_NAME) - return name ? ( - - ) : ( - cloneElement(icon, {...others, className, role: 'img'}) - ) + + const [Component, providedProps] = name + ? [AvatarFallbackName, {name, ...others}] + : [ + props => {icon}, + {...others, className, role: 'img'} + ] + + return } MoleculeAvatarFallback.displayName = 'MoleculeAvatarFallback' diff --git a/components/molecule/avatar/src/AvatarFallbackName/index.js b/components/molecule/avatar/src/AvatarFallbackName/index.js index 348e51e387..9394745b5f 100644 --- a/components/molecule/avatar/src/AvatarFallbackName/index.js +++ b/components/molecule/avatar/src/AvatarFallbackName/index.js @@ -1,6 +1,7 @@ import cx from 'classnames' import PropTypes from 'prop-types' +import useConvertStringToHex from '../useConvertStringToHex.js' import {BASE_CLASS_NAME} from './settings.js' const MoleculeAvatarFallbackName = ({ @@ -20,9 +21,16 @@ const MoleculeAvatarFallbackName = ({ ? `${firstName.charAt(0)}${lastName.charAt(0)}` : firstName.charAt(0) + const backgroundColor = useConvertStringToHex(nameProp) + return ( -
- {name.toUpperCase()} +
+ {name}
) } diff --git a/components/molecule/avatar/src/AvatarFallbackName/index.scss b/components/molecule/avatar/src/AvatarFallbackName/index.scss index d9b862461d..2311b9dc37 100644 --- a/components/molecule/avatar/src/AvatarFallbackName/index.scss +++ b/components/molecule/avatar/src/AvatarFallbackName/index.scss @@ -1,5 +1,11 @@ -$base-class-fallback-name: '#{$base-class}FallbackName'; +$base-class-fallback-name: #{$base-class}FallbackName; #{$base-class-fallback-name} { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; color: $c-avatar-fallback-name; + text-transform: uppercase; } diff --git a/components/molecule/avatar/src/index.js b/components/molecule/avatar/src/index.js index cae0d9d884..d583abd129 100644 --- a/components/molecule/avatar/src/index.js +++ b/components/molecule/avatar/src/index.js @@ -1,10 +1,4 @@ -import { - Children, - cloneElement, - forwardRef, - isValidElement, - useCallback -} from 'react' +import {forwardRef, useCallback} from 'react' import cx from 'classnames' import PropTypes from 'prop-types' @@ -14,6 +8,7 @@ import AtomSkeleton, { ATOM_SKELETON_ANIMATIONS, ATOM_SKELETON_VARIANTS } from '@s-ui/react-atom-skeleton' +import Injector from '@s-ui/react-primitive-injector' import AvatarBadge, { AVATAR_BADGE_PLACEMENTS, @@ -22,7 +17,6 @@ import AvatarBadge, { } from './AvatarBadge/index.js' import AvatarFallback from './AvatarFallback/index.js' import {AVATAR_SIZES, baseClassName} from './settings.js' -import useConvertStringToHex from './useConvertStringToHex.js' const MoleculeAvatar = forwardRef( ( @@ -43,16 +37,12 @@ const MoleculeAvatar = forwardRef( style, isLoading, imageProps = {}, - children: childrenProp, + children, ...others }, forwardedRef ) => { const className = cx(baseClassName, `${baseClassName}--${size}`) - const backgroundColor = useConvertStringToHex(name) - const children = Children.toArray(childrenProp) - .filter(child => isValidElement(child)) - .map(child => cloneElement(child, {size})) const renderContent = useCallback(() => { if (isLoading) { @@ -75,7 +65,7 @@ const MoleculeAvatar = forwardRef( ) : ( fallback )} - {!isLoading && children} + {!isLoading && {children}} ) }, [ @@ -90,15 +80,7 @@ const MoleculeAvatar = forwardRef( ]) return ( - + {renderContent()} ) diff --git a/components/molecule/avatar/src/styles/index.scss b/components/molecule/avatar/src/styles/index.scss index d22d7141d6..c5526badef 100644 --- a/components/molecule/avatar/src/styles/index.scss +++ b/components/molecule/avatar/src/styles/index.scss @@ -31,3 +31,5 @@ $base-class: '.sui-MoleculeAvatar'; } } } + +@import '../AvatarFallback/index.scss'; diff --git a/components/molecule/avatar/test/index.test.js b/components/molecule/avatar/test/index.test.js index a1bb6b638d..1955955a0b 100644 --- a/components/molecule/avatar/test/index.test.js +++ b/components/molecule/avatar/test/index.test.js @@ -192,7 +192,7 @@ describe(json.name, () => { const {getByText} = setup(props) // Then - expect(getByText('JS')).to.be.visible + expect(getByText('js')).to.be.visible }) describe('forwardRef', () => { diff --git a/components/molecule/breadcrumb/CHANGELOG.md b/components/molecule/breadcrumb/CHANGELOG.md index 0ebd69d8e1..62085b4bdf 100644 --- a/components/molecule/breadcrumb/CHANGELOG.md +++ b/components/molecule/breadcrumb/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# 1.27.0 (2022-10-06) + + + # 1.26.0 (2022-06-22) diff --git a/components/molecule/breadcrumb/package.json b/components/molecule/breadcrumb/package.json index 15bb8a6014..8e7eb41577 100644 --- a/components/molecule/breadcrumb/package.json +++ b/components/molecule/breadcrumb/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-molecule-breadcrumb", - "version": "1.26.0", + "version": "1.27.0", "description": "SUI Breadcrumb Basic", "main": "lib/index.js", "scripts": { diff --git a/components/molecule/buttonGroup/CHANGELOG.md b/components/molecule/buttonGroup/CHANGELOG.md index e4e0a6e28f..c4336c95f7 100644 --- a/components/molecule/buttonGroup/CHANGELOG.md +++ b/components/molecule/buttonGroup/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# 2.10.0 (2022-10-06) + + + # 2.9.0 (2022-06-22) diff --git a/components/molecule/buttonGroup/package.json b/components/molecule/buttonGroup/package.json index 51688c899c..2aa80ea4bd 100644 --- a/components/molecule/buttonGroup/package.json +++ b/components/molecule/buttonGroup/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-molecule-button-group", - "version": "2.9.0", + "version": "2.10.0", "description": "", "main": "lib/index.js", "scripts": { @@ -11,7 +11,8 @@ "dependencies": { "@s-ui/component-dependencies": "1", "@s-ui/react-atom-button": "1", - "@s-ui/react-primitive-polymorphic-element": "1" + "@s-ui/react-primitive-polymorphic-element": "1", + "@s-ui/react-primitive-injector": "1" }, "keywords": [], "author": "", diff --git a/components/molecule/buttonGroup/src/index.js b/components/molecule/buttonGroup/src/index.js index 67682c36b5..762b5e9e23 100644 --- a/components/molecule/buttonGroup/src/index.js +++ b/components/molecule/buttonGroup/src/index.js @@ -1,12 +1,13 @@ -import {Children, cloneElement} from 'react' +import {Children} from 'react' import cx from 'classnames' import PropTypes from 'prop-types' import {atomButtonDesigns, atomButtonSizes} from '@s-ui/react-atom-button' +import Injector from '@s-ui/react-primitive-injector' import Poly from '@s-ui/react-primitive-polymorphic-element' -import {BASE_CLASS, combineProps, isFunction} from './settings.js' +import {BASE_CLASS} from './settings.js' const getGroupPosition = ({groupPositions, numChildren}) => @@ -20,9 +21,9 @@ const MoleculeButtonGroup = ({ as = 'div', children, fullWidth, - size: sizeProp, - design: designProp, - negative: negativeProp, + size, + design, + negative, groupPositions, onClick, ...props @@ -35,26 +36,21 @@ const MoleculeButtonGroup = ({ const extendedChildren = Children.toArray(children) .filter(Boolean) .map((child, index) => { - const { - size: sizeChild, - design: designChild, - negative: negativeChild, - onClick: onClickChild - } = child.props const groupPosition = getGroupPositionByIndex(index) - const clickHandler = (event, ...args) => { - isFunction(onClickChild) && onClickChild(event, ...args) - isFunction(onClick) && onClick(event, ...args) - } - return cloneElement(child, { - ...props, - negative: combineProps(negativeChild, negativeProp), - size: combineProps(sizeChild, sizeProp), - design: combineProps(designChild, designProp), - groupPosition, - fullWidth, - onClick: clickHandler - }) + + return ( + + {child} + + ) }) return ( diff --git a/components/molecule/buttonGroup/src/settings.js b/components/molecule/buttonGroup/src/settings.js index 3d3293baf1..c5c2ae5da7 100644 --- a/components/molecule/buttonGroup/src/settings.js +++ b/components/molecule/buttonGroup/src/settings.js @@ -1,6 +1 @@ export const BASE_CLASS = 'sui-MoleculeButtonGroup' - -export const isFunction = fn => typeof fn === 'function' - -export const combineProps = (childProp, wrapperProp) => - childProp === undefined ? wrapperProp : childProp diff --git a/components/molecule/buttonGroupField/CHANGELOG.md b/components/molecule/buttonGroupField/CHANGELOG.md index 6ccce7b5de..b17a7556c9 100644 --- a/components/molecule/buttonGroupField/CHANGELOG.md +++ b/components/molecule/buttonGroupField/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# 1.9.0 (2022-10-06) + + + # 1.8.0 (2022-06-22) diff --git a/components/molecule/buttonGroupField/package.json b/components/molecule/buttonGroupField/package.json index 264b663169..392f478c1a 100644 --- a/components/molecule/buttonGroupField/package.json +++ b/components/molecule/buttonGroupField/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-molecule-button-group-field", - "version": "1.8.0", + "version": "1.9.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/molecule/checkboxField/CHANGELOG.md b/components/molecule/checkboxField/CHANGELOG.md index c528035bf4..d14ea7e0d4 100644 --- a/components/molecule/checkboxField/CHANGELOG.md +++ b/components/molecule/checkboxField/CHANGELOG.md @@ -1,5 +1,14 @@ # CHANGELOG +# 5.1.0 (2022-10-06) + + +### Features + +* **components/organism/nestedCheckboxes:** remove unnecesary lines ([6339679](https://github.com/SUI-Components/sui-components/commit/633967945b38296dd61597aa8c748a0950540b0a)) + + + # 5.0.0 (2022-07-18) diff --git a/components/molecule/checkboxField/package.json b/components/molecule/checkboxField/package.json index 74be695463..190008c691 100644 --- a/components/molecule/checkboxField/package.json +++ b/components/molecule/checkboxField/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-molecule-checkbox-field", - "version": "5.0.0", + "version": "5.1.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/molecule/drawer/CHANGELOG.md b/components/molecule/drawer/CHANGELOG.md index 0f595e2bea..1e514c97cc 100644 --- a/components/molecule/drawer/CHANGELOG.md +++ b/components/molecule/drawer/CHANGELOG.md @@ -1,5 +1,14 @@ # CHANGELOG +# 4.6.0 (2022-09-22) + + +### Bug Fixes + +* **components/molecule/drawer:** avoid add a listener for keydown that prevents the default behaviou ([2f3158a](https://github.com/SUI-Components/sui-components/commit/2f3158affa430b3c1f0d73288ebc82c6091ccb88)) + + + # 4.5.0 (2022-06-23) diff --git a/components/molecule/drawer/package.json b/components/molecule/drawer/package.json index db03b4559a..e4862ba94a 100644 --- a/components/molecule/drawer/package.json +++ b/components/molecule/drawer/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-molecule-drawer", - "version": "4.5.0", + "version": "4.6.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/molecule/drawer/src/index.js b/components/molecule/drawer/src/index.js index 3c06e837dc..f16c9aca4a 100644 --- a/components/molecule/drawer/src/index.js +++ b/components/molecule/drawer/src/index.js @@ -39,7 +39,6 @@ const MoleculeDrawer = forwardRef( useEventListener('keydown', event => { if (isOpen === false) return - event.preventDefault() if (event.key === 'Escape') { onClose(event, {isOpen: false}) } diff --git a/components/molecule/dropdownList/CHANGELOG.md b/components/molecule/dropdownList/CHANGELOG.md index 209f0eb3cf..d8598b0ad5 100644 --- a/components/molecule/dropdownList/CHANGELOG.md +++ b/components/molecule/dropdownList/CHANGELOG.md @@ -1,5 +1,24 @@ # CHANGELOG +# 2.1.0 (2022-10-04) + + +### Features + +* **components/molecule/dropdownList:** upgrade dropdownOption version ([6d1788c](https://github.com/SUI-Components/sui-components/commit/6d1788c8b2df523ab806dd88069a380292b48162)) + + + +# 1.33.0 (2022-10-03) + + +### Features + +* **components/molecule/dropdownList:** remove unnecesary code ([01d811e](https://github.com/SUI-Components/sui-components/commit/01d811ea44dbda9bd37c2a7edfc4a8016cc546a3)) +* **components/molecule/dropdownList:** replace cloneElement by Injector ([1b461d7](https://github.com/SUI-Components/sui-components/commit/1b461d7b8499e14c27327b9466918e3a055f972b)), closes [#2331](https://github.com/SUI-Components/sui-components/issues/2331) + + + # 1.32.0 (2022-07-26) diff --git a/components/molecule/dropdownList/demo/package.json b/components/molecule/dropdownList/demo/package.json index e2c1c085ae..fc3995f62c 100644 --- a/components/molecule/dropdownList/demo/package.json +++ b/components/molecule/dropdownList/demo/package.json @@ -11,6 +11,6 @@ "author": "", "license": "ISC", "dependencies": { - "@s-ui/react-molecule-dropdown-option": "1" + "@s-ui/react-molecule-dropdown-option": "2" } } diff --git a/components/molecule/dropdownList/package.json b/components/molecule/dropdownList/package.json index 2094664c37..4a7f5e8273 100644 --- a/components/molecule/dropdownList/package.json +++ b/components/molecule/dropdownList/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-molecule-dropdown-list", - "version": "1.32.0", + "version": "2.1.0", "description": "", "main": "lib/index.js", "scripts": { @@ -11,7 +11,8 @@ "dependencies": { "@s-ui/react-atom-input": "5", "@s-ui/react-hooks": "1", - "@s-ui/react-molecule-dropdown-option": "1", + "@s-ui/react-molecule-dropdown-option": "2", + "@s-ui/react-primitive-injector": "1", "lodash.isequal": "4.5" }, "peerDependencies": { diff --git a/components/molecule/dropdownList/src/ExtendedChildren.js b/components/molecule/dropdownList/src/ExtendedChildren.js index b0c2f7aabb..9ca263b6f5 100644 --- a/components/molecule/dropdownList/src/ExtendedChildren.js +++ b/components/molecule/dropdownList/src/ExtendedChildren.js @@ -1,42 +1,36 @@ -import {cloneElement} from 'react' - import isEqual from 'lodash.isequal' import PropTypes from 'prop-types' -const ExtendedChildren = ({ - value, - children, - onSelect: onSelectListHandler, - checkbox, - ...props -}) => { - const { - value: valueChild, - onSelect: onSelectOptionHandler, - selected: selectedChild, - checkbox: checkboxChild - } = children.props +import Injector from '@s-ui/react-primitive-injector' + +const ExtendedChildren = ({value, children, onSelect, checkbox, ...props}) => { + const {value: valueChild} = children.props const selected = Array.isArray(value) ? value.some(innerValue => isEqual(valueChild, innerValue)) : isEqual(value, valueChild) - const onSelectHandler = (...args) => { - typeof onSelectOptionHandler === 'function' && - onSelectOptionHandler(...args) - typeof onSelectListHandler === 'function' && onSelectListHandler(...args) - } - return cloneElement(children, { - ...props, - selected: selectedChild === undefined ? selected : selectedChild, - onSelect: onSelectHandler, - checkbox: checkboxChild === undefined ? checkbox : checkboxChild - }) + return ( + + {children} + + ) } ExtendedChildren.propTypes = { /** selected value */ value: PropTypes.oneOfType([PropTypes.string, PropTypes.array]), + /** checkbox contained in all DropdownOption **/ + checkbox: PropTypes.bool, /** each single node to be included in the list (MoleculeDropdownOption) */ - children: PropTypes.node + children: PropTypes.node, + /** callback on select option **/ + onSelect: PropTypes.func } export default ExtendedChildren diff --git a/components/molecule/dropdownOption/CHANGELOG.md b/components/molecule/dropdownOption/CHANGELOG.md index eb92ab45ca..82c2aba37b 100644 --- a/components/molecule/dropdownOption/CHANGELOG.md +++ b/components/molecule/dropdownOption/CHANGELOG.md @@ -1,5 +1,23 @@ # CHANGELOG +# 2.2.0 (2022-10-10) + + +### Features + +* **components/molecule/dropdownOption:** Fixed grammar error ([10ff698](https://github.com/SUI-Components/sui-components/commit/10ff698453177b950592d33e981cf964c69b05e4)), closes [#2371](https://github.com/SUI-Components/sui-components/issues/2371) + + + +# 2.1.0 (2022-10-04) + + +### Features + +* **components/molecule/dropdownOption:** change prop naming following conventions ([c6e900e](https://github.com/SUI-Components/sui-components/commit/c6e900e197cccbf7a85e55e7b794333e9da3e997)) + + + # 1.45.0 (2022-07-22) diff --git a/components/molecule/dropdownOption/package.json b/components/molecule/dropdownOption/package.json index 52ccd33a12..3947e7cc52 100644 --- a/components/molecule/dropdownOption/package.json +++ b/components/molecule/dropdownOption/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-molecule-dropdown-option", - "version": "1.45.0", + "version": "2.2.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/molecule/dropdownOption/src/handlersFactory/index.js b/components/molecule/dropdownOption/src/handlersFactory/index.js index 4de11bdb91..6037722ea5 100644 --- a/components/molecule/dropdownOption/src/handlersFactory/index.js +++ b/components/molecule/dropdownOption/src/handlersFactory/index.js @@ -1,7 +1,7 @@ const handlersFactory = ({ disabled = false, onSelect, - onSelectKey = 'Enter', + selectKey = 'Enter', selected, setInnerSelected, value @@ -14,11 +14,11 @@ const handlersFactory = ({ } const handleKeyDown = ev => { const {key} = ev - const isStringOnSelectKey = typeof onSelectKey === 'string' - const isPressedOnSelectKey = isStringOnSelectKey - ? key === onSelectKey - : onSelectKey.includes(key) - if (isPressedOnSelectKey && !disabled) { + const isStringSelectKey = typeof selectKey === 'string' + const isPressedSelectKey = isStringSelectKey + ? key === selectKey + : selectKey.includes(key) + if (isPressedSelectKey && !disabled) { ev.preventDefault() onSelect(ev, {value, selected: !selected}) setInnerSelected(!selected) diff --git a/components/molecule/dropdownOption/src/index.js b/components/molecule/dropdownOption/src/index.js index 2e30941887..52682b4962 100644 --- a/components/molecule/dropdownOption/src/index.js +++ b/components/molecule/dropdownOption/src/index.js @@ -35,7 +35,7 @@ const MoleculeDropdownOption = forwardRef( highlightQuery, highlightValue, innerRef, - onSelectKey, + selectKey, onSelect, selected, defaultSelected, @@ -72,7 +72,7 @@ const MoleculeDropdownOption = forwardRef( ]) const {handleClick, handleKeyDown, handleFocus} = handlersFactory({ disabled, - onSelectKey, + selectKey, onSelect, selected: innerSelected, setInnerSelected, @@ -164,7 +164,7 @@ MoleculeDropdownOption.propTypes = { children: PropTypes.node, /** Contains checkbox */ checkbox: PropTypes.bool, - /** Addtional props to set up the checkbox */ + /** Additional props to set up the checkbox */ checkboxProps: PropTypes.object, /** Is disabled */ disabled: PropTypes.bool, @@ -179,7 +179,10 @@ MoleculeDropdownOption.propTypes = { /** Text to be display if used with highlight query with custom content */ highlightValue: PropTypes.string, /* key to provoke the onClick callback. Valid any value defined here → https://www.w3.org/TR/uievents-key/#named-key-attribute-values */ - onSelectKey: PropTypes.oneOfType([PropTypes.string, PropTypes.array]), + selectKey: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.arrayOf(PropTypes.string) + ]), /** Custom ref handler that will be assigned to the "target" element */ innerRef: PropTypes.object, /** Text with css clamp = 2 */ @@ -193,7 +196,7 @@ MoleculeDropdownOption.defaultProps = { disabled: false, onSelect: () => {}, defaultSelected: false, - onSelectKey: 'Enter', + selectKey: 'Enter', innerRef: createRef() } export default MoleculeDropdownOption diff --git a/components/molecule/field/CHANGELOG.md b/components/molecule/field/CHANGELOG.md index 105767084a..940a3a788e 100644 --- a/components/molecule/field/CHANGELOG.md +++ b/components/molecule/field/CHANGELOG.md @@ -1,5 +1,32 @@ # CHANGELOG +# 1.37.0 (2022-10-06) + + +### Features + +* **components/molecule/field:** admit react nodes in error alert success and help text ([a946f54](https://github.com/SUI-Components/sui-components/commit/a946f54bae2d472c25efab4a5d9709c021c92834)) + + + +# 1.36.0 (2022-10-04) + + +### Bug Fixes + +* **components/molecule/field:** fix lint error ([9f56f8b](https://github.com/SUI-Components/sui-components/commit/9f56f8bfee58813850bc68492b8696d97098f4c4)) + + + +# 1.35.0 (2022-10-04) + + +### Features + +* **components/molecule/field:** refactor Label's conditional in field ([605fc5a](https://github.com/SUI-Components/sui-components/commit/605fc5a33e79f3280980ff8f913a73299f086598)), closes [#2334](https://github.com/SUI-Components/sui-components/issues/2334) + + + # 1.34.0 (2022-06-23) diff --git a/components/molecule/field/package.json b/components/molecule/field/package.json index b2203fffc7..d8914ce888 100644 --- a/components/molecule/field/package.json +++ b/components/molecule/field/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-molecule-field", - "version": "1.34.0", + "version": "1.37.0", "description": "", "main": "lib/index.js", "scripts": { @@ -11,7 +11,8 @@ "dependencies": { "@s-ui/react-atom-help-text": "1", "@s-ui/react-atom-label": "1", - "@s-ui/react-atom-validation-text": "1" + "@s-ui/react-atom-validation-text": "1", + "@s-ui/react-primitive-injector": "1" }, "peerDependencies": { "@s-ui/component-dependencies": "1" diff --git a/components/molecule/field/src/Label.js b/components/molecule/field/src/Label.js index c1ee638a32..6b65128cb6 100644 --- a/components/molecule/field/src/Label.js +++ b/components/molecule/field/src/Label.js @@ -1,41 +1,18 @@ -import {cloneElement} from 'react' import {isElement} from 'react-is' import PropTypes from 'prop-types' -import AtomLabel, {AtomLabelTypes} from '@s-ui/react-atom-label' +import AtomLabel from '@s-ui/react-atom-label' +import Injector from '@s-ui/react-primitive-injector' import {CLASS_NODE_LABEL_CONTAINER} from './config.js' -const MoleculeLabel = ({ - label, - nodeLabel, - type: typeValidationLabel, - name, - onClick -}) => { +const MoleculeLabel = ({label, nodeLabel, ...props}) => { const innerLabel = () => { - if (label) { - return isElement(label) ? ( - cloneElement(label, { - type: typeValidationLabel, - name, - onClick - }) - ) : ( - - ) - } else if (nodeLabel) { - return cloneElement(nodeLabel, { - type: typeValidationLabel, - name, - onClick - }) + if ((label && isElement(label)) || (!label && nodeLabel)) { + return {!label ? nodeLabel : label} + } else if (label) { + return } return null } @@ -44,10 +21,7 @@ const MoleculeLabel = ({ MoleculeLabel.propTypes = { label: PropTypes.string, - nodeLabel: PropTypes.element, - type: PropTypes.oneOf(Object.values(AtomLabelTypes)), - name: PropTypes.string, - onClick: PropTypes.func + nodeLabel: PropTypes.element } export default MoleculeLabel diff --git a/components/molecule/field/src/index.js b/components/molecule/field/src/index.js index ab11d8a920..e27892a0dd 100644 --- a/components/molecule/field/src/index.js +++ b/components/molecule/field/src/index.js @@ -140,16 +140,32 @@ MoleculeField.propTypes = { name: PropTypes.string.isRequired, /** Success message to display when success state */ - successText: PropTypes.oneOfType([PropTypes.element, PropTypes.bool]), + successText: PropTypes.oneOfType([ + PropTypes.element, + PropTypes.bool, + PropTypes.node + ]), /** Error message to display when error state */ - errorText: PropTypes.oneOfType([PropTypes.element, PropTypes.bool]), + errorText: PropTypes.oneOfType([ + PropTypes.element, + PropTypes.bool, + PropTypes.node + ]), /** Error message to display when alert state */ - alertText: PropTypes.oneOfType([PropTypes.element, PropTypes.bool]), + alertText: PropTypes.oneOfType([ + PropTypes.element, + PropTypes.bool, + PropTypes.node + ]), /** Help Text to display */ - helpText: PropTypes.oneOfType([PropTypes.element, PropTypes.bool]), + helpText: PropTypes.oneOfType([ + PropTypes.element, + PropTypes.bool, + PropTypes.node + ]), /** Boolean to decide if elements should be set inline */ inline: PropTypes.bool, diff --git a/components/molecule/imageEditor/CHANGELOG.md b/components/molecule/imageEditor/CHANGELOG.md index 408ded1e65..d5c2d27aef 100644 --- a/components/molecule/imageEditor/CHANGELOG.md +++ b/components/molecule/imageEditor/CHANGELOG.md @@ -1,5 +1,14 @@ # CHANGELOG +# 1.10.0 (2022-09-20) + + +### Features + +* **components/molecule/imageEditor:** Add a SCSS token to customize imageEditor labels color ([714c393](https://github.com/SUI-Components/sui-components/commit/714c393e87c94deb5fe2cb2f8f968deb7ce1224f)) + + + # 1.9.0 (2022-09-05) diff --git a/components/molecule/imageEditor/package.json b/components/molecule/imageEditor/package.json index 5c0b5dbb00..5430f3b573 100644 --- a/components/molecule/imageEditor/package.json +++ b/components/molecule/imageEditor/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-molecule-image-editor", - "version": "1.9.0", + "version": "1.10.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/molecule/imageEditor/src/styles/index.scss b/components/molecule/imageEditor/src/styles/index.scss index 2a118fb847..5d95678039 100644 --- a/components/molecule/imageEditor/src/styles/index.scss +++ b/components/molecule/imageEditor/src/styles/index.scss @@ -19,7 +19,7 @@ $base-class: '.sui-MoleculeImageEditor'; &-label { align-items: center; - color: $c-gray; + color: $c-molecule-image-editor-label; display: flex; margin-right: $m-l; diff --git a/components/molecule/imageEditor/src/styles/settings.scss b/components/molecule/imageEditor/src/styles/settings.scss index 2704749c75..b9552c9a4d 100644 --- a/components/molecule/imageEditor/src/styles/settings.scss +++ b/components/molecule/imageEditor/src/styles/settings.scss @@ -1,3 +1,4 @@ +$c-molecule-image-editor-label: $c-gray !default; $h-molecule-image-editor: 300px !default; $mb-molecule-image-editor-crop: $m-xxl !default; $mt-molecule-image-editor-slider: $m-xl !default; diff --git a/components/molecule/inputTags/src/config.js b/components/molecule/inputTags/src/config.js index 3e08958bd6..4e1ee64da9 100644 --- a/components/molecule/inputTags/src/config.js +++ b/components/molecule/inputTags/src/config.js @@ -1,3 +1,6 @@ +import {inputSizes} from '@s-ui/react-atom-input' +import {atomTagSizes} from '@s-ui/react-atom-tag' + export const BASE_CLASS = 'sui-AtomInput' export const CLASS_TAGS = `${BASE_CLASS}--withTags` export const CLASS_TAGS_FOCUS = `${CLASS_TAGS}--focus` @@ -12,4 +15,15 @@ export const isDuplicate = (values, newValue) => { return upperTags.includes(newValue.toUpperCase()) } +export const getInputToTagSize = inputSize => { + const conversor = { + [inputSizes.XSMALL]: atomTagSizes.XSMALL, + [inputSizes.SMALL]: atomTagSizes.SMALL, + [inputSizes.MEDIUM]: atomTagSizes.MEDIUM, + [inputSizes.LARGE]: atomTagSizes.LARGE, + [inputSizes.XLARGE]: atomTagSizes.XLARGE + } + return conversor[inputSize] +} + export const isFunction = fn => typeof fn === 'function' diff --git a/components/molecule/inputTags/src/index.js b/components/molecule/inputTags/src/index.js index 823c82910d..79da8ff01b 100644 --- a/components/molecule/inputTags/src/index.js +++ b/components/molecule/inputTags/src/index.js @@ -14,6 +14,7 @@ import { CLASS_TAGS_ERROR, CLASS_TAGS_FOCUS, CLASS_TAGS_SUCCESS, + getInputToTagSize, isDuplicate, isFunction } from './config.js' @@ -136,10 +137,11 @@ const MoleculeInputTags = forwardRef( label })} label={label} - size={atomTagSizes.SMALL} + size={getInputToTagSize(size)} responsive readOnly={readOnly} disabled={disabled} + isFitted /> ) })} @@ -156,6 +158,7 @@ const MoleculeInputTags = forwardRef( noBorder readOnly={readOnly} disabled={disabled} + size={size} placeholder={isEmpty ? placeholder : undefined} /> )} diff --git a/components/molecule/inputTags/src/styles/index.scss b/components/molecule/inputTags/src/styles/index.scss index e92b1804b2..f499e479bb 100644 --- a/components/molecule/inputTags/src/styles/index.scss +++ b/components/molecule/inputTags/src/styles/index.scss @@ -10,6 +10,7 @@ $class-input: '#{$base-class}-input'; display: flex; flex-wrap: wrap; padding: 0 0 0 $p-m; + gap: $p-base; &--focus { @include sui-atom-input-input-focus; @@ -22,6 +23,9 @@ $class-input: '#{$base-class}-input'; background-color: transparent; } } + &[aria-readonly='true'] { + background-color: $bgc-input-tag-read-only; + } & #{$class-input} { flex: 1; diff --git a/components/molecule/inputTags/src/styles/settings.scss b/components/molecule/inputTags/src/styles/settings.scss index 8fff321a6d..6c637513b9 100644 --- a/components/molecule/inputTags/src/styles/settings.scss +++ b/components/molecule/inputTags/src/styles/settings.scss @@ -1,3 +1,4 @@ $sizes-atom-input-paddings: m $p-m, s $p-s !default; -$bg-input-tag-disable: $c-gray-light-4 !default; +$bg-input-tag-disable: $bgc-atom-input-disabled; +$bgc-input-tag-read-only: $bgc-atom-input-read-only; $bdrs-molecule-input-tags: 0 !default; diff --git a/components/molecule/inputTags/test/index.test.js b/components/molecule/inputTags/test/index.test.js index 4078321014..8d9b8ff67d 100644 --- a/components/molecule/inputTags/test/index.test.js +++ b/components/molecule/inputTags/test/index.test.js @@ -127,7 +127,7 @@ describe(json.name, () => { sinon.match({ tags: [], name: props.name, - value: value + value }) ) }) @@ -161,7 +161,7 @@ describe(json.name, () => { sinon.match({ tags: props.tags, name: props.name, - value: value + value }) ) }) diff --git a/components/molecule/notification/CHANGELOG.md b/components/molecule/notification/CHANGELOG.md index 92fa1df83c..9f672b78ff 100644 --- a/components/molecule/notification/CHANGELOG.md +++ b/components/molecule/notification/CHANGELOG.md @@ -1,5 +1,26 @@ # CHANGELOG +# 1.34.0 (2022-10-01) + + +### Features + +* **components/molecule/notification:** Remove -system ([b84c52c](https://github.com/SUI-Components/sui-components/commit/b84c52c956db4b709039dc6739f60c48b5427a79)) + + + +# 1.33.0 (2022-09-19) + + +### Features + +* **components/molecule/notification:** create new variable to define background positive variation ([b77ec9b](https://github.com/SUI-Components/sui-components/commit/b77ec9ba9fd3eb965df744311d09bec895184490)) +* **components/molecule/notification:** refactor to use background colors set at theme to create pos ([70bd3b0](https://github.com/SUI-Components/sui-components/commit/70bd3b07365967e52164b10a9a2a00e43240a209)) +* **Root:** Delete undefined dependencies ([c145905](https://github.com/SUI-Components/sui-components/commit/c145905350328925ba6fda2a462d7f8b508c8ea0)) +* **Root:** Merge commit ([d3735d0](https://github.com/SUI-Components/sui-components/commit/d3735d0644332e674d5a5b6291680697f0d6f7c4)) + + + # 1.32.0 (2022-06-23) diff --git a/components/molecule/notification/package.json b/components/molecule/notification/package.json index e491defd02..472d434e86 100644 --- a/components/molecule/notification/package.json +++ b/components/molecule/notification/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-molecule-notification", - "version": "1.32.0", + "version": "1.34.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/molecule/notification/src/_settings.scss b/components/molecule/notification/src/_settings.scss index b2365b1e3b..5f341033b8 100644 --- a/components/molecule/notification/src/_settings.scss +++ b/components/molecule/notification/src/_settings.scss @@ -1,3 +1,5 @@ +@use 'sass:map'; + $w-notification: 100% !default; $z-notification: 1000 !default; $mh-notification: 500px !default; @@ -30,7 +32,35 @@ $notification-type-colors: ( success: $c-success, warning: $c-alert, error: $c-error, - system: $c-system + system: $c-black +) !default; + +$notification-type-positive-colors: ( + info: + color-variation( + map.get($notification-type-colors, 'info'), + $c-light-step * 2 + ), + success: + color-variation( + map.get($notification-type-colors, 'success'), + $c-light-step * 2 + ), + warning: + color-variation( + map.get($notification-type-colors, 'warning'), + $c-light-step * 2 + ), + error: + color-variation( + map.get($notification-type-colors, 'error'), + $c-light-step * 2 + ), + system: + color-variation( + map.get($notification-type-colors, 'system'), + $c-light-step * 2 + ) ) !default; $notification-type-font-colors: ( @@ -47,7 +77,7 @@ $notification-type-positive-text-colors: ( success: $c-success, warning: $c-alert, error: $c-error, - system: $c-system + system: $c-black ) !default; // Positive Icon color @@ -56,7 +86,7 @@ $notification-type-positive-icon-colors: ( success: $c-success, warning: $c-alert, error: $c-error, - system: $c-system + system: $c-black ) !default; // Effects diff --git a/components/molecule/notification/src/styles/index.scss b/components/molecule/notification/src/styles/index.scss index 128a6e6acb..c377eba752 100644 --- a/components/molecule/notification/src/styles/index.scss +++ b/components/molecule/notification/src/styles/index.scss @@ -85,9 +85,9 @@ $base-class: '.sui-MoleculeNotification'; } // Type variations - @each $key, $value in $notification-type-colors { + @each $key, $value in $notification-type-positive-colors { &#{$base-class}--#{$key} { - background: color-variation($value, $c-light-step * 2); + background: #{$value}; } } } diff --git a/components/molecule/photoUploader/demo/package.json b/components/molecule/photoUploader/demo/package.json index 3347b27be7..9a78a21592 100644 --- a/components/molecule/photoUploader/demo/package.json +++ b/components/molecule/photoUploader/demo/package.json @@ -12,7 +12,7 @@ "license": "ISC", "dependencies": { "@s-ui/react-atom-button": "1", - "@s-ui/react-molecule-dropdown-option": "1", + "@s-ui/react-molecule-dropdown-option": "2", "@s-ui/react-molecule-select": "1" } } diff --git a/components/molecule/rating/CHANGELOG.md b/components/molecule/rating/CHANGELOG.md index 4b041efc8a..096cc33ff1 100644 --- a/components/molecule/rating/CHANGELOG.md +++ b/components/molecule/rating/CHANGELOG.md @@ -1,5 +1,14 @@ # CHANGELOG +# 1.21.0 (2022-10-01) + + +### Features + +* **components/molecule/rating:** Remove -system ([dc7bd06](https://github.com/SUI-Components/sui-components/commit/dc7bd06a57c74e6112589ec6a97b738f090632cc)) + + + # 1.20.0 (2022-06-23) diff --git a/components/molecule/rating/package.json b/components/molecule/rating/package.json index 6a0fc59052..15a9f5ed3e 100644 --- a/components/molecule/rating/package.json +++ b/components/molecule/rating/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-molecule-rating", - "version": "1.20.0", + "version": "1.21.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/molecule/rating/src/styles/settings.scss b/components/molecule/rating/src/styles/settings.scss index 55e823696e..068bc9b402 100644 --- a/components/molecule/rating/src/styles/settings.scss +++ b/components/molecule/rating/src/styles/settings.scss @@ -21,5 +21,5 @@ $m-l-molecule-small-rating-stars: $m-l-molecule-rating-stars !default; $p-molecule-rating-label: $p-m !default; $p-molecule-rating-stars: 0 !default; -$c-molecule-rating-svg: $c-system !default; +$c-molecule-rating-svg: $c-black !default; $c-molecule-rating-svg-hover: $c-primary !default; diff --git a/components/molecule/select/CHANGELOG.md b/components/molecule/select/CHANGELOG.md index 1c9e1a45d7..b8246404aa 100644 --- a/components/molecule/select/CHANGELOG.md +++ b/components/molecule/select/CHANGELOG.md @@ -1,5 +1,24 @@ # CHANGELOG +# 1.57.0 (2022-10-11) + + +### Bug Fixes + +* **components/molecule/select:** accept onblur prop ([ad3f8fb](https://github.com/SUI-Components/sui-components/commit/ad3f8fb08be2260ffba01fce160c4d3df3814754)), closes [#2282](https://github.com/SUI-Components/sui-components/issues/2282) + + + +# 1.56.0 (2022-10-04) + + +### Features + +* **components/molecule/select:** set new prop name following conventions ([484a999](https://github.com/SUI-Components/sui-components/commit/484a9999fa2eace0fcaf27f73d37872b3d1ca208)) +* **components/molecule/select:** upgrade dropdownOption version ([e05859e](https://github.com/SUI-Components/sui-components/commit/e05859e6135cf0400b616705dce25c0f00a89c2f)) + + + # 1.55.0 (2022-07-29) diff --git a/components/molecule/select/demo/package.json b/components/molecule/select/demo/package.json index 0ba8ff5989..b562f0365e 100644 --- a/components/molecule/select/demo/package.json +++ b/components/molecule/select/demo/package.json @@ -12,6 +12,6 @@ "license": "ISC", "dependencies": { "@s-ui/hoc": "1", - "@s-ui/react-molecule-dropdown-option": "1" + "@s-ui/react-molecule-dropdown-option": "2" } } diff --git a/components/molecule/select/package.json b/components/molecule/select/package.json index c9887d8beb..9a2b056e30 100644 --- a/components/molecule/select/package.json +++ b/components/molecule/select/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-molecule-select", - "version": "1.55.0", + "version": "1.57.0", "description": "", "main": "lib/index.js", "scripts": { @@ -12,7 +12,7 @@ "@s-ui/js": "2", "@s-ui/react-atom-input": "5", "@s-ui/react-hooks": "1", - "@s-ui/react-molecule-dropdown-list": "1", + "@s-ui/react-molecule-dropdown-list": "2", "@s-ui/react-molecule-input-tags": "2", "@s-ui/react-primitive-injector": "1" }, diff --git a/components/molecule/select/src/components/MoleculeInputSelect.js b/components/molecule/select/src/components/MoleculeInputSelect.js index a4ac2f9d48..624e007a9e 100644 --- a/components/molecule/select/src/components/MoleculeInputSelect.js +++ b/components/molecule/select/src/components/MoleculeInputSelect.js @@ -20,8 +20,12 @@ const MoleculeInputSelect = props => { return (
- {children} - {iconArrow} + {iconArrow}} + {...props} + > + {children} +
) } diff --git a/components/molecule/select/src/components/MultipleSelection.js b/components/molecule/select/src/components/MultipleSelection.js index 17a718a43a..3d24d7e315 100644 --- a/components/molecule/select/src/components/MultipleSelection.js +++ b/components/molecule/select/src/components/MultipleSelection.js @@ -26,6 +26,7 @@ const MoleculeSelectFieldMultiSelection = props => { } = props const tags = values.map(value => optionsData[value]) + const isFull = () => maxTags && tags?.length >= maxTags const handleMultiSelection = (ev, {value: valueOptionSelected}) => { const handleToggle = ev => { @@ -42,8 +43,6 @@ const MoleculeSelectFieldMultiSelection = props => { const addToValues = () => [...values, valueOptionSelected] - const isFull = () => maxTags && tags?.length >= maxTags - if (isValueSelectedAlreadySelected()) { onChange(ev, {value: removeFromValues()}) } else if (!isFull()) { @@ -82,7 +81,7 @@ const MoleculeSelectFieldMultiSelection = props => { diff --git a/components/molecule/select/src/components/SingleSelection.js b/components/molecule/select/src/components/SingleSelection.js index 3cd9c56e94..34fc386db7 100644 --- a/components/molecule/select/src/components/SingleSelection.js +++ b/components/molecule/select/src/components/SingleSelection.js @@ -48,7 +48,7 @@ const MoleculeSelectSingleSelection = props => { size={selectSize} tabIndex={tabIndex} > - + { const { + onBlur, isOpen, onToggle, children, @@ -39,6 +40,7 @@ const MoleculeSelect = forwardRef((props, forwardedRef) => { refMoleculeSelect: refMoleculeSelectFromProps, 'aria-label': ariaLabel } = props + const refMoleculeSelect = useRef(refMoleculeSelectFromProps) const refsMoleculeSelectOptions = useRef([]) const ref = useMergeRefs(forwardedRef, refMoleculeSelect) @@ -57,7 +59,7 @@ const MoleculeSelect = forwardRef((props, forwardedRef) => { refsMoleculeSelectOptions.current[index] = createRef() return cloneElement(child, { innerRef: refsMoleculeSelectOptions.current[index], - onSelectKey: keysSelection + selectKey: keysSelection }) }) @@ -125,7 +127,10 @@ const MoleculeSelect = forwardRef((props, forwardedRef) => { } } - const handleFocusOut = () => setFocus(false) + const handleFocusOut = event => { + onBlur(event) + setFocus(false) + } const handleFocusIn = () => !disabled && setFocus(true) @@ -185,6 +190,9 @@ MoleculeSelect.propTypes = { /** if list of options is displayed or not */ isOpen: PropTypes.bool, + /** callback onBlur to be triggered when focused outside of the input */ + onBlur: PropTypes.func, + /** callback when arrow up/down is clicked → to show/hide list of options */ onToggle: PropTypes.func, @@ -232,6 +240,7 @@ MoleculeSelect.propTypes = { } MoleculeSelect.defaultProps = { + onBlur: () => {}, disabled: false, keysSelection: SELECTION_KEYS, onChange: () => {}, diff --git a/components/molecule/select/src/styles/index.scss b/components/molecule/select/src/styles/index.scss index 3318f7ac1a..f051f800eb 100644 --- a/components/molecule/select/src/styles/index.scss +++ b/components/molecule/select/src/styles/index.scss @@ -13,7 +13,7 @@ $class-select-atom-input-tags: '#{$class-select-atom-input}--withTags'; z-index: $z-select-list; } - &-inputSelect { + #{$class-input} { &-container { align-items: center; border: $bd-select; @@ -23,19 +23,13 @@ $class-select-atom-input-tags: '#{$class-select-atom-input}--withTags'; #{$class-select-atom-input-input} { caret-color: transparent; - background: $bgc-atom-input; - color: $c-atom-input; } - .is-disabled & { + #{$class-select-atom-input}--is-disabled { cursor: default; #{$class-select-atom-input-input} { -webkit-text-fill-color: $c-molecule-select-disabled; - - background: $bg-molecule-select-disabled; - border: $bd-molecule-select-disabled; - color: $c-molecule-select-disabled; } #{$class-input}-arrow { @@ -49,18 +43,9 @@ $class-select-atom-input-tags: '#{$class-select-atom-input}--withTags'; opacity: 1; -webkit-appearance: none; } - - #{$class-select-atom-input-input}:first-child, - #{$class-select-atom-input-tags} { - padding-right: $pr-select-atom-input-tags; - } } &-arrow { - height: $h-select-list-arrow; - margin-left: $mr-select-list-arrow; - width: $w-select-list-arrow; - &--down { transform: rotate(0deg); } diff --git a/components/molecule/select/test/index.test.js b/components/molecule/select/test/index.test.js index e1ec6bb9fc..d39dd6633c 100644 --- a/components/molecule/select/test/index.test.js +++ b/components/molecule/select/test/index.test.js @@ -9,6 +9,9 @@ import ReactDOM from 'react-dom' import chai, {expect} from 'chai' import chaiDOM from 'chai-dom' +import sinon from 'sinon' + +import userEvents from '@testing-library/user-event' import json from '../package.json' import * as pkg from '../src/index.js' @@ -85,6 +88,24 @@ describe(json.name, () => { // Then expect(findClassName(container.innerHTML)).to.be.null }) + + it('should call onBlur callback', () => { + const spy = sinon.spy() + + const props = { + onBlur: spy + } + + // When + const {getByRole} = setup(props) + const textBox = getByRole('textbox') + + userEvents.click(textBox) + userEvents.tab() + + // Then + sinon.assert.calledOnce(spy) + }) }) describe('moleculeSelectDropdownListSizes', () => { diff --git a/components/molecule/selectField/demo/package.json b/components/molecule/selectField/demo/package.json index 6aaac19f57..f66d073fa0 100644 --- a/components/molecule/selectField/demo/package.json +++ b/components/molecule/selectField/demo/package.json @@ -12,7 +12,7 @@ "license": "ISC", "dependencies": { "@s-ui/hoc": "1", - "@s-ui/react-molecule-dropdown-option": "1", + "@s-ui/react-molecule-dropdown-option": "2", "@s-ui/react-molecule-field": "1", "@s-ui/react-molecule-select": "1" } diff --git a/components/molecule/selectPopover/demo/package.json b/components/molecule/selectPopover/demo/package.json index fcdfd82e15..c12143910c 100644 --- a/components/molecule/selectPopover/demo/package.json +++ b/components/molecule/selectPopover/demo/package.json @@ -12,7 +12,7 @@ "license": "ISC", "dependencies": { "@s-ui/react-icons": "1", - "@s-ui/react-molecule-dropdown-option": "1", + "@s-ui/react-molecule-dropdown-option": "2", "@s-ui/react-molecule-modal": "1", "@s-ui/react-molecule-select": "1" } diff --git a/components/molecule/tabs/CHANGELOG.md b/components/molecule/tabs/CHANGELOG.md index bd792b53b1..8d9f1081b2 100644 --- a/components/molecule/tabs/CHANGELOG.md +++ b/components/molecule/tabs/CHANGELOG.md @@ -1,5 +1,29 @@ # CHANGELOG +# 2.37.0 (2022-10-13) + + +### Features + +* **components/molecule/tabs:** add id prop to make tabs uniques ([b90231d](https://github.com/SUI-Components/sui-components/commit/b90231dc07db9ef4dcc7943fafe2c634b38a1070)) +* **components/molecule/tabs:** add pull request feedback ([208e0be](https://github.com/SUI-Components/sui-components/commit/208e0bed5aa055ae796fc934d79c334b17acc1b4)) +* **components/molecule/tabs:** pull request feedback ([f32e971](https://github.com/SUI-Components/sui-components/commit/f32e9718a114586ba6e9139406c0db8f330100fd)) + + + +# 2.36.0 (2022-09-22) + + +### Features + +* **components/molecule/tabs:** add orientation aria attribute ([71296a3](https://github.com/SUI-Components/sui-components/commit/71296a319365c6109c7e9aaa2077933ceb80d09e)) +* **components/molecule/tabs:** Change tabs to be able to modify current active tabs by updating pro ([3f6a23e](https://github.com/SUI-Components/sui-components/commit/3f6a23e3538970054c289b4607c806baa72c6922)) +* **components/molecule/tabs:** control/uncontroll active tabs ([7945c33](https://github.com/SUI-Components/sui-components/commit/7945c339e5a145a2d593b2f68d4344aea2100f7b)) +* **components/molecule/tabs:** make tabs accesibles ([b4ebaf3](https://github.com/SUI-Components/sui-components/commit/b4ebaf3f60efa0bb51422059bf631f9e1e076666)) +* **components/molecule/tabs:** remove unnecesary prop injected ([4b67d13](https://github.com/SUI-Components/sui-components/commit/4b67d1304388ec589112eb3bd4d24767c8733367)) + + + # 2.35.0 (2022-06-23) diff --git a/components/molecule/tabs/demo/Articles/ArticleActiveTabs.js b/components/molecule/tabs/demo/Articles/ArticleActiveTabs.js new file mode 100644 index 0000000000..4effa6569a --- /dev/null +++ b/components/molecule/tabs/demo/Articles/ArticleActiveTabs.js @@ -0,0 +1,111 @@ +import {useState} from 'react' + +import MoleculeTabs, {MoleculeTab} from 'components/molecule/tabs/src/index.js' +import PropTypes from 'prop-types' + +import { + Article, + Code, + H2, + H3, + Paragraph, + RadioButton, + RadioButtonGroup +} from '@s-ui/documentation-library' + +import Content from '../components/Content.js' +import {CLASS_DEMO_CONTENT_TAB} from '../config.js' + +const tabsNumber = 5 + +const ArticleActiveTabs = ({className}) => { + const [activeTabIndex, setActiveTabIndex] = useState(3) + return ( +
+

Controlled and uncontrolled active tabs

+ + Under activeTabIndex (number, default 1) developer can + control the active tab. Use the defaultActiveTabIndex for + defining the initial tab uncontrolled. + +

Uncontrolled

+ + {Array(tabsNumber) + .fill(true) + .map((v, index) => ( + Label {index + 1}} + numTab={index + 1} + disabled={index + 1 === 4} + > + + + ))} + +

Controlled

+ { + setActiveTabIndex(value) + }} + > + {Array(tabsNumber) + .fill(true) + .map((v, index) => ( + {`tab ${index + 1}`} + ))} + + { + setActiveTabIndex(numTab) + }} + > + {Array(tabsNumber) + .fill(true) + .map((v, index) => ( + Label {index + 1}} + numTab={index + 1} + disabled={index + 1 === 4} + > + + + ))} + +
+ ) +} + +ArticleActiveTabs.displayName = 'ArticleActiveTabs' + +ArticleActiveTabs.propTypes = { + className: PropTypes.string +} + +export default ArticleActiveTabs diff --git a/components/molecule/tabs/demo/index.js b/components/molecule/tabs/demo/index.js index 2ad6efb130..9315dffb17 100644 --- a/components/molecule/tabs/demo/index.js +++ b/components/molecule/tabs/demo/index.js @@ -1,5 +1,6 @@ import {H1, Paragraph} from '@s-ui/documentation-library' +import ArticleActiveTabs from './Articles/ArticleActiveTabs.js' import ArticleDefault from './Articles/ArticleDefault.js' import ArticleIconsCounters from './Articles/ArticleIconsCounters.js' import ArticleType from './Articles/ArticleType.js' @@ -18,6 +19,8 @@ const Demo = () => {
+ +

diff --git a/components/molecule/tabs/package.json b/components/molecule/tabs/package.json index 98db3ad4ad..1eb73a36c8 100644 --- a/components/molecule/tabs/package.json +++ b/components/molecule/tabs/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-molecule-tabs", - "version": "2.35.0", + "version": "2.37.0", "description": "", "main": "lib/index.js", "scripts": { diff --git a/components/molecule/tabs/src/components/MoleculeTab.js b/components/molecule/tabs/src/components/MoleculeTab.js index 52636797f5..bd256c93dc 100644 --- a/components/molecule/tabs/src/components/MoleculeTab.js +++ b/components/molecule/tabs/src/components/MoleculeTab.js @@ -21,6 +21,7 @@ const MoleculeTab = forwardRef( count, disabled, icon, + id = 'molecule-tab-content', isIntersecting, label, numTab, @@ -54,6 +55,9 @@ const MoleculeTab = forwardRef( className={className} onClick={handleChange} ref={useMergeRefs(innerRef, forwardedRef)} + role="tab" + aria-selected={active} + aria-controls={`${id}-${numTab}`} > {icon && {icon}} {!isNaN(count) && {count}} @@ -73,6 +77,9 @@ MoleculeTab.propTypes = { /** icon (React component) */ icon: PropTypes.node, + /** id used to make tabs unique per page */ + id: PropTypes.string, + /** count to display */ count: PropTypes.string, diff --git a/components/molecule/tabs/src/components/MoleculeTabs.js b/components/molecule/tabs/src/components/MoleculeTabs.js index 754f1ef41e..c82d5d1da2 100644 --- a/components/molecule/tabs/src/components/MoleculeTabs.js +++ b/components/molecule/tabs/src/components/MoleculeTabs.js @@ -16,6 +16,7 @@ import { const MoleculeTabs = ({ autoScrollIntoView = true, children, + id = 'molecule-tab-content', onChange, type, variant @@ -25,6 +26,7 @@ const MoleculeTabs = ({ [`${BASE_CLASS}--${type}`]: type }) const childrenArray = Children.toArray(children) + const isVerticalOrientation = type === TYPES.VERTICAL const [isIntersecting, outerRef] = useOnScreen() @@ -36,26 +38,39 @@ const MoleculeTabs = ({ autoScrollIntoView, isIntersecting, numTab, + id, onChange }) }) const activeTabContent = childrenArray.reduce((activeContent, child) => { if (child) { - const {children: childrenChild, active} = child.props - return active ? childrenChild : activeContent + const {children: childrenChild, active, numTab} = child.props + + if (active) { + return ( +
+ {childrenChild} +
+ ) + } } return activeContent }, null) return (
-
    +
      {extendedChildren}
    - {activeTabContent ? ( -
    {activeTabContent}
    - ) : null} + {activeTabContent}
) } @@ -69,6 +84,9 @@ MoleculeTabs.propTypes = { /** children */ children: PropTypes.any, + /** id used to make tabs unique */ + id: PropTypes.string, + /** onChange */ onChange: PropTypes.func, diff --git a/components/molecule/tabs/src/index.js b/components/molecule/tabs/src/index.js index 436bb17b80..0b8d360e73 100644 --- a/components/molecule/tabs/src/index.js +++ b/components/molecule/tabs/src/index.js @@ -1,47 +1,49 @@ -import {Children, cloneElement, useEffect, useState} from 'react' +import {Children, cloneElement} from 'react' import PropTypes from 'prop-types' +import useControlledState from '@s-ui/react-hooks/lib/useControlledState' + import MoleculeTab from './components/MoleculeTab.js' import MoleculeTabs from './components/MoleculeTabs.js' import {TYPES, VARIANTS} from './config.js' -const MoleculeTabsWithStateActive = ({children, onChange, ...props}) => { - const [activeTab, setActiveTab] = useState(null) - - useEffect(() => { - Children.forEach(children, (child, index) => { - if (child) { - const {active} = child.props - if (active) setActiveTab(index + 1) - } - }) - }, []) // eslint-disable-line - - const extendedChildren = () => { - return Children.toArray(children) - .filter(Boolean) - .map((child, index) => { - const numTab = index + 1 - const active = activeTab === numTab - return cloneElement(child, {active}) - }) - } +const MoleculeTabsWithStateActive = ({ + children, + activeTabIndex: activeTabIndexProp, + defaultActiveTabIndex: defaultActiveTabIndexProp = 1, + onChange, + ...props +}) => { + const [activeTab, setActiveTab] = useControlledState( + activeTabIndexProp, + defaultActiveTabIndexProp + ) - const handleChange = (e, {numTab}) => { - setActiveTab(numTab) - typeof onChange === 'function' && onChange(e, {numTab}) + const handleChange = (e, {numTab: tabIndex}) => { + setActiveTab(tabIndex) + typeof onChange === 'function' && onChange(e, {numTab: tabIndex}) } return ( - {extendedChildren()} + {Children.toArray(children) + .filter(Boolean) + .map((child, index) => + cloneElement(child, {active: activeTab === index + 1}) + )} ) } MoleculeTabsWithStateActive.displayName = 'MoleculeTabsWithStateActive' MoleculeTabsWithStateActive.propTypes = { + /** defines the active tab */ + activeTabIndex: PropTypes.number, + + /** defines the initial active tab */ + defaultActiveTabIndex: PropTypes.number, + /** children of the component */ children: PropTypes.element, diff --git a/components/molecule/tabs/test/index.test.js b/components/molecule/tabs/test/index.test.js index 36633d62dd..0128f895b0 100644 --- a/components/molecule/tabs/test/index.test.js +++ b/components/molecule/tabs/test/index.test.js @@ -10,6 +10,8 @@ import ReactDOM from 'react-dom' import chai, {expect} from 'chai' import chaiDOM from 'chai-dom' +import {fireEvent} from '@testing-library/react' + import json from '../package.json' import * as pkg from '../src/index.js' @@ -148,6 +150,42 @@ describe(json.name, () => { count[2].toString() ) }) + + it('should switch content when tab 2 is clicked', () => { + // Given + const library = pkg + const {MoleculeTab} = library + const expectedContent1 = 'Content 1' + const expectedContent2 = 'Content 2' + const props = { + children: [ + + {expectedContent1} + , + + {expectedContent2} + + ] + } + + // When + const {getByRole} = setup(props) + const tab1 = getByRole('tab', {name: 'Tab 1'}) + expect(tab1).to.have.attribute('aria-selected', 'true') + + const content1 = getByRole('tabpanel') + expect(content1.innerHTML).to.equal(expectedContent1) + + // Click on second tab + const tab2 = getByRole('tab', {name: 'Tab 2'}) + expect(tab2).to.have.attribute('aria-selected', 'false') + fireEvent.click(tab2) + const content2 = getByRole('tabpanel') + + // Then + expect(tab2).to.have.attribute('aria-selected', 'true') + expect(content2.innerHTML).to.equal(expectedContent2) + }) }) describe('moleculeTabsTypes', () => {