From 1994c98063c27a41797dec01f2ca9fcbe33ceab0 Mon Sep 17 00:00:00 2001 From: Israel Lavi Date: Mon, 21 May 2018 17:42:00 +0300 Subject: init commit onap ui Change-Id: I1dace78817dbba752c550c182dfea118b4a38646 Issue-ID: SDC-1350 Signed-off-by: Israel Lavi --- .babelrc | 4 + .editorconfig | 18 + .eslintrc | 135 +++++ .gitignore | 31 ++ .gitreview | 4 + .ng2-component-lab/lab-configuration.module.ts | 36 ++ .ng2-component-lab/ng2-component-lab.config.js | 11 + .ng2-component-lab/ng2-component-lab.scss | 58 +++ .../themes/ng2-component-lab-theme-1802.scss | 58 +++ .npmignore | 6 + .storybook/config.js | 8 + .storybook/storybook.scss | 231 ++++++++ .storybook/typography.scss | 30 ++ .storybook/webpack.config.js | 39 ++ .travis.yml | 31 ++ LICENSE.TXT | 20 + README.md | 111 ++++ assets/README.md | 9 + assets/icons/angleDoubleLeft.svg | 15 + assets/icons/angleDoubleRight.svg | 11 + assets/icons/angleLeft.svg | 9 + assets/icons/angleRight.svg | 9 + assets/icons/artifacts.svg | 1 + assets/icons/back.svg | 6 + assets/icons/base.svg | 1 + assets/icons/calendar.svg | 1 + assets/icons/caretDown.svg | 6 + assets/icons/check.svg | 9 + assets/icons/checkCircle.svg | 1 + assets/icons/chevronDown.svg | 9 + assets/icons/chevronUp.svg | 9 + assets/icons/close.svg | 10 + assets/icons/download.svg | 1 + assets/icons/empty.txt | 0 assets/icons/env.svg | 1 + assets/icons/error.svg | 1 + assets/icons/errorCircle.svg | 1 + assets/icons/exclamationTriangleFull.svg | 9 + assets/icons/exclamationTriangleLine.svg | 25 + assets/icons/expand.svg | 1 + assets/icons/filter.svg | 9 + assets/icons/locked.svg | 3 + assets/icons/module.svg | 1 + assets/icons/nestedHeat.svg | 1 + assets/icons/network.svg | 1 + assets/icons/notificationBell.svg | 18 + assets/icons/notificationFullBell.svg | 11 + assets/icons/others.svg | 1 + assets/icons/pencil.svg | 17 + assets/icons/plus.svg | 9 + assets/icons/plusCircle.svg | 9 + assets/icons/plusThin.svg | 7 + assets/icons/proceedToOverview.svg | 1 + assets/icons/questionMark.svg | 1 + assets/icons/search.svg | 8 + assets/icons/sliders.svg | 19 + assets/icons/trashO.svg | 17 + assets/icons/unlocked.svg | 3 + assets/icons/upload.svg | 1 + assets/icons/user.svg | 10 + assets/icons/vendor.svg | 1 + assets/icons/versionControllerCommit.svg | 1 + assets/icons/versionControllerLockClosed.svg | 17 + assets/icons/versionControllerLockOpen.svg | 17 + assets/icons/versionControllerPermissions.svg | 1 + assets/icons/versionControllerRevert.svg | 14 + assets/icons/versionControllerSave.svg | 10 + assets/icons/versionControllerSubmit.svg | 10 + assets/icons/versionControllerSync.svg | 1 + assets/icons/versionControllerUndo.svg | 1 + assets/icons/viewModule.svg | 9 + assets/icons/vlm.svg | 1 + assets/icons/vsp.svg | 1 + assets/icons/zip.svg | 1 + assets/images/empty.txt | 0 assets/images/illustration.png | Bin 0 -> 29534 bytes assets/images/logo_onap.png | Bin 0 -> 21360 bytes assets/images/logo_onap_2017.png | Bin 0 -> 21360 bytes assets/sdc-icons/README.md | 9 + assets/sdc-icons/alert-triangle-o.svg | 3 + assets/sdc-icons/alert-triangle.svg | 5 + assets/sdc-icons/api-o.svg | 3 + assets/sdc-icons/arrow2-right-child.svg | 3 + assets/sdc-icons/arrow2-right.svg | 3 + assets/sdc-icons/arrow3-down-o.svg | 3 + assets/sdc-icons/arrow3-up-o.svg | 3 + assets/sdc-icons/attachment.svg | 3 + assets/sdc-icons/bedge.svg | 5 + assets/sdc-icons/browse.svg | 3 + assets/sdc-icons/calendar-o.svg | 3 + assets/sdc-icons/camera-o.svg | 3 + assets/sdc-icons/caret1-down-o.svg | 3 + assets/sdc-icons/caret2-right-circle-o.svg | 3 + assets/sdc-icons/caret2-right-circle.svg | 4 + assets/sdc-icons/caret3-right.svg | 3 + assets/sdc-icons/close.svg | 3 + assets/sdc-icons/commit-o.svg | 3 + assets/sdc-icons/components/checkbox_checked.svg | 3 + assets/sdc-icons/components/checkbox_disabled.svg | 9 + assets/sdc-icons/components/radio_checked .svg | 6 + assets/sdc-icons/components/radio_disabled.svg | 6 + assets/sdc-icons/composition-o.svg | 3 + assets/sdc-icons/copy-o.svg | 3 + assets/sdc-icons/deployment-artifacts-o.svg | 3 + assets/sdc-icons/description-o.svg | 3 + assets/sdc-icons/distributed.svg | 3 + assets/sdc-icons/download-o.svg | 3 + assets/sdc-icons/edit-file-o.svg | 3 + assets/sdc-icons/edit-o.svg | 3 + assets/sdc-icons/expand-o.svg | 3 + assets/sdc-icons/eye-o.svg | 3 + assets/sdc-icons/filter-o.svg | 3 + assets/sdc-icons/info-circle-o.svg | 3 + assets/sdc-icons/info-circle.svg | 5 + assets/sdc-icons/info-square-o.svg | 3 + assets/sdc-icons/inputs-o.svg | 3 + assets/sdc-icons/locked.svg | 3 + assets/sdc-icons/minus-circle.svg | 3 + assets/sdc-icons/minus.svg | 3 + assets/sdc-icons/notifications-o.svg | 3 + assets/sdc-icons/plus-circle-o.svg | 3 + assets/sdc-icons/plus-circle.svg | 5 + assets/sdc-icons/plus.svg | 3 + assets/sdc-icons/profile-o.svg | 3 + assets/sdc-icons/profiles-o.svg | 3 + assets/sdc-icons/question-mark-circle-o.svg | 3 + assets/sdc-icons/question-mark-circle.svg | 5 + assets/sdc-icons/req-capabilities-o.svg | 3 + assets/sdc-icons/revert-o.svg | 3 + assets/sdc-icons/save-o.svg | 3 + assets/sdc-icons/search-o.svg | 3 + assets/sdc-icons/settings-o.svg | 3 + assets/sdc-icons/spinner.svg | 50 ++ assets/sdc-icons/success-circle-o.svg | 3 + assets/sdc-icons/success.svg | 3 + assets/sdc-icons/sync-o.svg | 3 + assets/sdc-icons/trash-o.svg | 3 + assets/sdc-icons/undo-o.svg | 3 + assets/sdc-icons/unlocked-o.svg | 3 + assets/sdc-icons/upload-o.svg | 3 + assets/sdc-icons/v-circle-o.svg | 3 + assets/sdc-icons/v-circle.svg | 3 + assets/sdc-icons/x-circle-o.svg | 3 + assets/sdc-icons/x-circle.svg | 5 + components/accordion/accordion-basic.html | 22 + components/accordion/accordion.scss | 50 ++ components/autocomplete/_autocomplete.scss | 43 ++ components/autocomplete/autocomlete-close.html | 22 + components/autocomplete/autocomplete-open.html | 24 + components/button/_button.scss | 168 ++++++ components/button/button-link-auto.html | 3 + components/button/button-link-disabled.html | 3 + components/button/button-link-extra-small.html | 3 + components/button/button-link-large.html | 3 + components/button/button-link-medium.html | 3 + components/button/button-link-small.html | 3 + components/button/button-link.html | 3 + components/button/button-primary-auto.html | 3 + components/button/button-primary-disabled.html | 3 + components/button/button-primary-extra-small.html | 3 + components/button/button-primary-large.html | 3 + components/button/button-primary-medium.html | 3 + components/button/button-primary-small.html | 3 + components/button/button-primary.html | 3 + components/button/button-secondary-auto.html | 3 + components/button/button-secondary-disabled.html | 3 + .../button/button-secondary-extra-small.html | 3 + components/button/button-secondary-large.html | 3 + components/button/button-secondary-medium.html | 3 + components/button/button-secondary-small.html | 3 + components/button/button-secondary.html | 3 + components/checkbox/_checkbox.scss | 66 +++ components/checkbox/checkbox-checked.html | 6 + components/checkbox/checkbox-disabled-checked.html | 6 + components/checkbox/checkbox-disabled.html | 6 + components/checkbox/checkbox-unchecked.html | 6 + components/checklist/_checklist.scss | 21 + .../checklist/checklist-with-checked-items.html | 24 + .../checklist/checklist-with-disabled-items.html | 25 + components/checklist/multi-levels-checklist.html | 50 ++ components/checklist/simple-checklist.html | 24 + components/dropdown/_dropdown.scss | 346 ++++++++++++ components/dropdown/dropdown-disabled.html | 11 + components/dropdown/dropdown-groups.html | 10 + components/dropdown/dropdown-requiered.html | 18 + components/dropdown/dropdown.html | 9 + components/filter-bar/_filter-bar.scss | 51 ++ components/filter-bar/filter-bar-with-text.html | 16 + components/filter-bar/filter-bar.html | 17 + components/icon/_icon.scss | 250 +++++++++ components/input/_input.scss | 78 +++ components/input/input-disabled.html | 4 + components/input/input-error.html | 17 + components/input/input-number.html | 6 + components/input/input-placeholder.html | 4 + components/input/input-required.html | 4 + components/input/input-view-only.html | 4 + components/input/input.html | 8 + components/menu/_menu.scss | 68 +++ components/menu/popup-menu.html | 8 + components/menu/relative-popup-menu.html | 8 + components/modal/_modal.scss | 194 +++++++ components/modal/alert-modal.html | 45 ++ components/modal/custom-modal.html | 27 + components/modal/error-modal.html | 32 ++ components/modal/standard-modal.html | 46 ++ components/notification/_notification.scss | 59 +++ components/notification/notification-info.html | 3 + .../_notifications-container.scss | 8 + components/panel/basic-panel.html | 21 + components/panel/panel.scss | 8 + components/radio/_radio.scss | 69 +++ components/radio/radio-checked.html | 4 + components/radio/radio-disabled-checked.html | 4 + components/radio/radio-disabled.html | 4 + components/radio/radio-unchecked.html | 4 + components/radioGroup/_radioGroup.scss | 20 + components/radioGroup/radio-group-disabled.html | 13 + components/radioGroup/radio-group-no-title.html | 12 + components/radioGroup/radio-group-value.html | 13 + components/radioGroup/radio-group.html | 13 + components/search-bar/_search-bar.scss | 61 +++ components/search-bar/search-bar-with-text.html | 17 + components/search-bar/search-bar.html | 16 + components/tabs/tabs-disabled.html | 8 + components/tabs/tabs-header.html | 8 + components/tabs/tabs-menu.html | 8 + components/tabs/tabs.scss | 35 ++ components/tag-cloud/_tag-cloud.scss | 116 +++++ components/tag-cloud/disabled-list.html | 31 ++ .../tag-cloud/list-with-active-add-button.html | 50 ++ .../tag-cloud/list-with-some-read-only-items.html | 61 +++ components/tag-cloud/list-with-unique-error.html | 52 ++ components/tag-cloud/simple-list.html | 50 ++ components/tile/_tile.scss | 172 ++++++ components/tile/tile-without-footer.html | 14 + components/tile/vendor-tile.html | 26 + components/tile/vfc-tile.html | 17 + components/tile/vlm-tile.html | 22 + components/tile/vsp-tile.html | 17 + components/tooltip/_tooltip.scss | 124 +++++ components/validation/_validation.scss | 9 + demo/.gitignore | 7 + demo/README.md | 35 ++ demo/assests/README.md | 9 + demo/assests/icons/empty.txt | 0 demo/assests/icons/locked.svg | 39 ++ demo/assests/icons/plus.svg | 9 + demo/assests/icons/unlocked.svg | 39 ++ demo/assests/icons/vendor.svg | 1 + demo/assests/icons/vlm.svg | 1 + demo/assests/icons/vsp.svg | 1 + demo/assests/images/empty.txt | 0 demo/components/button.html | 15 + demo/components/colors.html | 26 + demo/components/tiles-generic.html | 106 ++++ demo/components/tiles.html | 105 ++++ demo/index.css | 178 +++++++ demo/index.html | 53 ++ demo/index.js | 84 +++ demo/package.json | 23 + designs/README.md | 3 + jest.config.js | 30 ++ json-typing.d.ts | 4 + karma.conf.js | 87 ++++ karma.entry.js | 66 +++ ng2-component-lab.webpack.config.js | 62 +++ package.json | 133 +++++ pom.xml | 162 ++++++ rollup.angular.module.config.js | 33 ++ rollup.angular.umd.config.js | 38 ++ src/README.md | 9 + src/angular/accordion/accordion.component.html.ts | 21 + src/angular/accordion/accordion.component.ts | 27 + src/angular/accordion/accordion.module.ts | 23 + .../animations/animation-directives.module.ts | 19 + .../animations/ripple-click.animation.directive.ts | 47 ++ .../autocomplete/autocomplete.component.html.ts | 14 + src/angular/autocomplete/autocomplete.component.ts | 114 ++++ src/angular/autocomplete/autocomplete.module.ts | 23 + src/angular/autocomplete/autocomplete.pipe.ts | 16 + src/angular/buttons/button.component.html.ts | 15 + src/angular/buttons/button.component.ts | 62 +++ src/angular/buttons/buttons.module.ts | 21 + src/angular/checklist/checklist.component.html.ts | 15 + src/angular/checklist/checklist.component.ts | 50 ++ src/angular/checklist/checklist.module.ts | 11 + src/angular/checklist/models/Checklist.ts | 18 + src/angular/checklist/models/ChecklistItem.ts | 17 + src/angular/common/enums.ts | 34 ++ src/angular/common/index.ts | 3 + src/angular/components.ts | 50 ++ src/angular/filterbar/filter-bar.component.html.ts | 30 ++ src/angular/filterbar/filter-bar.component.ts | 30 ++ src/angular/filterbar/filter-bar.module.ts | 17 + .../checkbox/checkbox.component.html.ts | 8 + .../checkbox/checkbox.component.spec.ts | 37 ++ .../form-elements/checkbox/checkbox.component.ts | 21 + .../form-elements/dropdown/dropdown-models.ts | 18 + .../dropdown/dropdown-trigger.directive.ts | 17 + .../dropdown/dropdown.component.html.ts | 59 +++ .../dropdown/dropdown.component.spec.ts | 71 +++ .../form-elements/dropdown/dropdown.component.ts | 149 ++++++ src/angular/form-elements/form-elements.module.ts | 38 ++ .../form-elements/input/input.component.html.ts | 19 + src/angular/form-elements/input/input.component.ts | 54 ++ .../form-elements/radios/radio-button.model.ts | 15 + .../radios/radio-buttons-group.component.html.ts | 20 + .../radios/radio-buttons-group.component.spec.ts | 52 ++ .../radios/radio-buttons-group.component.ts | 52 ++ .../validation/validatable.component.ts | 25 + .../validation/validatable.interface.ts | 5 + .../validation/validation-group.component.html.ts | 3 + .../validation/validation-group.component.ts | 47 ++ .../validation/validation.component.html.ts | 3 + .../validation/validation.component.ts | 79 +++ .../form-elements/validation/validation.module.ts | 35 ++ .../validators/base.validator.component.html.ts | 10 + .../validators/base.validator.component.ts | 25 + .../validators/custom.validator.component.ts | 23 + .../validators/regex.validator.component.ts | 24 + .../validators/required.validator.component.ts | 25 + .../validation/validators/validator.interface.ts | 3 + src/angular/index.ts | 68 +++ .../infinite-scroll/infinite-scroll.directive.ts | 35 ++ .../infinite-scroll/infinite-scroll.module.ts | 13 + src/angular/modals/modal-button.component.ts | 29 ++ src/angular/modals/modal-close-button.component.ts | 34 ++ src/angular/modals/modal.component.html.ts | 38 ++ src/angular/modals/modal.component.spec.ts | 105 ++++ src/angular/modals/modal.component.ts | 96 ++++ src/angular/modals/modal.module.ts | 33 ++ src/angular/modals/modal.service.ts | 100 ++++ src/angular/modals/models/modal-config.ts | 44 ++ src/angular/ng1.module.ts | 135 +++++ .../container/notifcontainer.component.html.ts | 6 + .../container/notifcontainer.component.ts | 31 ++ ...notification-inner-content-example.component.ts | 21 + src/angular/notifications/notification.module.ts | 29 ++ .../notification/notification.component.html.ts | 19 + .../notification/notification.component.ts | 42 ++ .../services/notifications.service.ts | 41 ++ .../notifications/utilities/notification.config.ts | 30 ++ .../popup-menu/popup-menu-item.component.spec.ts | 25 + .../popup-menu/popup-menu-item.component.ts | 34 ++ .../popup-menu/popup-menu-list.component.ts | 65 +++ src/angular/popup-menu/popup-menu.module.ts | 21 + src/angular/searchbar/search-bar.component.html.ts | 19 + src/angular/searchbar/search-bar.component.ts | 19 + src/angular/searchbar/search-bar.module.ts | 17 + .../svg-icon/svg-icon-label.component.html.ts | 6 + src/angular/svg-icon/svg-icon-label.component.ts | 26 + src/angular/svg-icon/svg-icon.component.html.ts | 3 + src/angular/svg-icon/svg-icon.component.ts | 77 +++ src/angular/svg-icon/svg-icon.module.ts | 21 + src/angular/tabs/children/tab.component.html.ts | 5 + src/angular/tabs/children/tab.component.ts | 16 + src/angular/tabs/tabs.component.html.ts | 14 + src/angular/tabs/tabs.component.ts | 41 ++ src/angular/tabs/tabs.module.ts | 23 + src/angular/tag-cloud/tag-cloud.component.html.ts | 30 ++ src/angular/tag-cloud/tag-cloud.component.ts | 46 ++ src/angular/tag-cloud/tag-cloud.module.ts | 21 + .../tag-cloud/tag-item/tag-item.component.html.ts | 16 + .../tag-cloud/tag-item/tag-item.component.ts | 15 + .../tiles/children/tile-content.component.ts | 10 + .../tiles/children/tile-footer.component.ts | 10 + .../tiles/children/tile-header.component.ts | 10 + src/angular/tiles/tile.component.html.ts | 5 + src/angular/tiles/tile.component.ts | 11 + src/angular/tiles/tile.module.ts | 27 + src/angular/tooltip/tooltip-template.component.ts | 20 + src/angular/tooltip/tooltip.directive.ts | 459 ++++++++++++++++ src/angular/tooltip/tooltip.module.ts | 17 + .../utils/create-dynamic-component.service.ts | 101 ++++ src/react/Accordion.js | 40 ++ src/react/Button.js | 37 ++ src/react/Checkbox.js | 45 ++ src/react/Checklist.js | 43 ++ src/react/Input.js | 88 ++++ src/react/Modal.js | 55 ++ src/react/ModalBody.js | 19 + src/react/ModalFooter.js | 36 ++ src/react/ModalHeader.js | 41 ++ src/react/ModalTitle.js | 19 + src/react/Panel.js | 18 + src/react/PopupMenu.js | 39 ++ src/react/PopupMenuItem.js | 34 ++ src/react/Portal.js | 52 ++ src/react/Radio.js | 58 +++ src/react/RadioGroup.js | 40 ++ src/react/SVGIcon.js | 47 ++ src/react/Tab.js | 20 + src/react/TabPane.js | 12 + src/react/Tabs.js | 29 ++ src/react/Tile.js | 33 ++ src/react/TileFooter.js | 10 + src/react/TileFooterCell.js | 7 + src/react/TileInfo.js | 10 + src/react/TileInfoLine.js | 7 + src/react/index.js | 74 +++ src/style/scss/_common.scss | 7 + src/style/scss/_components.scss | 22 + src/style/scss/angular/_svg_icon.scss | 210 ++++++++ src/style/scss/angular/_tooltip_custom_style.scss | 9 + src/style/scss/common/_animation.scss | 149 ++++++ src/style/scss/common/_icons.scss | 19 + src/style/scss/common/_normalize.scss | 578 +++++++++++++++++++++ src/style/scss/common/_typography.scss | 96 ++++ src/style/scss/common/base.scss | 96 ++++ src/style/scss/common/mixins.scss | 337 ++++++++++++ src/style/scss/common/variables.scss | 35 ++ src/style/scss/style.scss | 6 + src/style/scss/themes/1802/_components.scss | 23 + src/style/scss/themes/1802/button.scss | 148 ++++++ src/style/scss/themes/1802/modal.scss | 193 +++++++ src/style/scss/themes/1802/style.scss | 5 + src/style/scss/themes/1802/tabs.scss | 39 ++ stories/README.md | 9 + .../ng2-component-lab/accordion.component.exp.ts | 146 ++++++ .../autocomplete.component.exp.ts | 77 +++ stories/ng2-component-lab/button.component.exp.ts | 164 ++++++ .../ng2-component-lab/checkbox.component.exp.ts | 33 ++ .../ng2-component-lab/checklist.component.exp.ts | 213 ++++++++ stories/ng2-component-lab/colors.component.exp.ts | 42 ++ stories/ng2-component-lab/components.module.ts | 45 ++ .../components/colors-table.component.ts | 26 + .../components/modal-consumer.component.ts | 106 ++++ .../modal-inner-content-example.component.ts | 16 + .../components/notifications-example.component.ts | 57 ++ .../components/svg-icons-table.component.ts | 189 +++++++ .../ng2-component-lab/dropdown.component.exp.ts | 195 +++++++ .../ng2-component-lab/filter-bar.component.exp.ts | 56 ++ .../infinite-scroll.component.exp.ts | 166 ++++++ stories/ng2-component-lab/input.component.exp.ts | 79 +++ stories/ng2-component-lab/modals.component.exp.ts | 126 +++++ .../notification.component.exp.ts | 11 + .../ng2-component-lab/pipes/search-filter-pipe.ts | 15 + .../ng2-component-lab/popup-menu.component.exp.ts | 104 ++++ stories/ng2-component-lab/radio.component.exp.ts | 179 +++++++ .../ng2-component-lab/search-bar.component.exp.ts | 19 + .../ng2-component-lab/svg-icon.component.exp.ts | 14 + stories/ng2-component-lab/tabs.component.exp.ts | 28 + .../ng2-component-lab/tag-cloud.component.exp.ts | 61 +++ stories/ng2-component-lab/tiles.component.exp.ts | 194 +++++++ stories/ng2-component-lab/tooltip.directive.exp.ts | 231 ++++++++ stories/ng2-component-lab/utils/mock.json | 6 + stories/ng2-component-lab/utils/pipes/keys.pipe.ts | 13 + .../ng2-component-lab/validation.component.exp.ts | 162 ++++++ stories/react/Accordion.stories.js | 16 + stories/react/Checkbox.stories.js | 33 ++ stories/react/Checklist.stories.js | 65 +++ stories/react/Colors.stories.js | 53 ++ stories/react/Input.stories.js | 51 ++ stories/react/Modal.stories.js | 133 +++++ stories/react/Panel.stories.js | 22 + stories/react/PopupMenu.stories.js | 37 ++ stories/react/Radio.stories.js | 33 ++ stories/react/RadioGroup.stories.js | 34 ++ stories/react/SVGIcon.stories.js | 103 ++++ stories/react/Tabs.stories.js | 48 ++ stories/react/Tiles.stories.js | 89 ++++ stories/react/Typography.stories.js | 62 +++ stories/react/buttons/LinkButtons.stories.js | 49 ++ stories/react/buttons/PrimaryButtons.stories.js | 49 ++ stories/react/buttons/SecondaryButtons.stories.js | 49 ++ stories/react/index.js | 66 +++ stories/react/utils/BeautifyHTML.js | 33 ++ stories/react/utils/Examples.js | 23 + stories/react/utils/InsertSVGIcons.js | 15 + stories/react/utils/SourceToggle.js | 73 +++ stories/react/utils/components/DropdownMenu.js | 14 + stories/react/utils/jsxToString.js | 74 +++ test/react/Accordion.spec.js | 11 + test/react/Button.spec.js | 77 +++ test/react/Checkbox.spec.js | 60 +++ test/react/Checklist.spec.js | 64 +++ test/react/Input.spec.js | 69 +++ test/react/Modal.spec.js | 68 +++ test/react/ModalBody.spec.js | 11 + test/react/ModalFooter.spec.js | 11 + test/react/ModalHeader.spec.js | 11 + test/react/ModalTitle.spec.js | 11 + test/react/Panel.spec.js | 11 + test/react/PopupMenu.spec.js | 62 +++ test/react/PopupMenuItem.spec.js | 56 ++ test/react/Portal.spec.js | 10 + test/react/Radio.spec.js | 60 +++ test/react/RadioGroup.spec.js | 69 +++ test/react/Tabs.spec.js | 55 ++ test/react/Tile.spec.js | 30 ++ test/react/__snapshots__/Accordion.spec.js.snap | 32 ++ test/react/__snapshots__/Button.spec.js.snap | 163 ++++++ test/react/__snapshots__/Checkbox.spec.js.snap | 49 ++ test/react/__snapshots__/Checklist.spec.js.snap | 165 ++++++ test/react/__snapshots__/Input.spec.js.snap | 179 +++++++ test/react/__snapshots__/Modal.spec.js.snap | 9 + test/react/__snapshots__/ModalBody.spec.js.snap | 7 + test/react/__snapshots__/ModalFooter.spec.js.snap | 17 + test/react/__snapshots__/ModalHeader.spec.js.snap | 24 + test/react/__snapshots__/ModalTitle.spec.js.snap | 9 + test/react/__snapshots__/Panel.spec.js.snap | 9 + test/react/__snapshots__/PopupMenu.spec.js.snap | 26 + .../react/__snapshots__/PopupMenuItem.spec.js.snap | 19 + test/react/__snapshots__/Radio.spec.js.snap | 49 ++ test/react/__snapshots__/RadioGroup.spec.js.snap | 60 +++ test/react/__snapshots__/Tabs.spec.js.snap | 52 ++ test/react/__snapshots__/Tile.spec.js.snap | 108 ++++ test/react/utils/htmlLoader.js | 7 + test/react/utils/svgMock.js | 3 + tsconfig.angular.build-es5.json | 24 + tsconfig.json | 31 ++ tslint.json | 77 +++ utils/build-demo.js | 73 +++ utils/create-icon-map.js | 26 + utils/create-svg-icons-map.js | 95 ++++ utils/index-for-gh-pages.html | 98 ++++ utils/main-page.html | 62 +++ utils/scripts/map-sources.js | 9 + version.properties | 13 + webpack.build.config.js | 63 +++ webpack/helpers.js | 10 + webpack/webpack.test.js | 71 +++ 523 files changed, 18987 insertions(+) create mode 100644 .babelrc create mode 100644 .editorconfig create mode 100644 .eslintrc create mode 100644 .gitignore create mode 100644 .gitreview create mode 100644 .ng2-component-lab/lab-configuration.module.ts create mode 100644 .ng2-component-lab/ng2-component-lab.config.js create mode 100644 .ng2-component-lab/ng2-component-lab.scss create mode 100644 .ng2-component-lab/themes/ng2-component-lab-theme-1802.scss create mode 100644 .npmignore create mode 100644 .storybook/config.js create mode 100644 .storybook/storybook.scss create mode 100644 .storybook/typography.scss create mode 100644 .storybook/webpack.config.js create mode 100644 .travis.yml create mode 100644 LICENSE.TXT create mode 100644 README.md create mode 100644 assets/README.md create mode 100644 assets/icons/angleDoubleLeft.svg create mode 100644 assets/icons/angleDoubleRight.svg create mode 100644 assets/icons/angleLeft.svg create mode 100644 assets/icons/angleRight.svg create mode 100644 assets/icons/artifacts.svg create mode 100644 assets/icons/back.svg create mode 100644 assets/icons/base.svg create mode 100644 assets/icons/calendar.svg create mode 100644 assets/icons/caretDown.svg create mode 100644 assets/icons/check.svg create mode 100644 assets/icons/checkCircle.svg create mode 100644 assets/icons/chevronDown.svg create mode 100644 assets/icons/chevronUp.svg create mode 100644 assets/icons/close.svg create mode 100644 assets/icons/download.svg create mode 100644 assets/icons/empty.txt create mode 100644 assets/icons/env.svg create mode 100644 assets/icons/error.svg create mode 100644 assets/icons/errorCircle.svg create mode 100644 assets/icons/exclamationTriangleFull.svg create mode 100644 assets/icons/exclamationTriangleLine.svg create mode 100644 assets/icons/expand.svg create mode 100644 assets/icons/filter.svg create mode 100644 assets/icons/locked.svg create mode 100644 assets/icons/module.svg create mode 100644 assets/icons/nestedHeat.svg create mode 100644 assets/icons/network.svg create mode 100644 assets/icons/notificationBell.svg create mode 100644 assets/icons/notificationFullBell.svg create mode 100644 assets/icons/others.svg create mode 100644 assets/icons/pencil.svg create mode 100644 assets/icons/plus.svg create mode 100644 assets/icons/plusCircle.svg create mode 100644 assets/icons/plusThin.svg create mode 100644 assets/icons/proceedToOverview.svg create mode 100644 assets/icons/questionMark.svg create mode 100644 assets/icons/search.svg create mode 100644 assets/icons/sliders.svg create mode 100644 assets/icons/trashO.svg create mode 100644 assets/icons/unlocked.svg create mode 100644 assets/icons/upload.svg create mode 100644 assets/icons/user.svg create mode 100644 assets/icons/vendor.svg create mode 100644 assets/icons/versionControllerCommit.svg create mode 100644 assets/icons/versionControllerLockClosed.svg create mode 100644 assets/icons/versionControllerLockOpen.svg create mode 100644 assets/icons/versionControllerPermissions.svg create mode 100644 assets/icons/versionControllerRevert.svg create mode 100644 assets/icons/versionControllerSave.svg create mode 100644 assets/icons/versionControllerSubmit.svg create mode 100644 assets/icons/versionControllerSync.svg create mode 100644 assets/icons/versionControllerUndo.svg create mode 100644 assets/icons/viewModule.svg create mode 100644 assets/icons/vlm.svg create mode 100644 assets/icons/vsp.svg create mode 100644 assets/icons/zip.svg create mode 100644 assets/images/empty.txt create mode 100644 assets/images/illustration.png create mode 100644 assets/images/logo_onap.png create mode 100644 assets/images/logo_onap_2017.png create mode 100644 assets/sdc-icons/README.md create mode 100644 assets/sdc-icons/alert-triangle-o.svg create mode 100644 assets/sdc-icons/alert-triangle.svg create mode 100644 assets/sdc-icons/api-o.svg create mode 100644 assets/sdc-icons/arrow2-right-child.svg create mode 100644 assets/sdc-icons/arrow2-right.svg create mode 100644 assets/sdc-icons/arrow3-down-o.svg create mode 100644 assets/sdc-icons/arrow3-up-o.svg create mode 100644 assets/sdc-icons/attachment.svg create mode 100644 assets/sdc-icons/bedge.svg create mode 100644 assets/sdc-icons/browse.svg create mode 100644 assets/sdc-icons/calendar-o.svg create mode 100644 assets/sdc-icons/camera-o.svg create mode 100644 assets/sdc-icons/caret1-down-o.svg create mode 100644 assets/sdc-icons/caret2-right-circle-o.svg create mode 100644 assets/sdc-icons/caret2-right-circle.svg create mode 100644 assets/sdc-icons/caret3-right.svg create mode 100644 assets/sdc-icons/close.svg create mode 100644 assets/sdc-icons/commit-o.svg create mode 100644 assets/sdc-icons/components/checkbox_checked.svg create mode 100644 assets/sdc-icons/components/checkbox_disabled.svg create mode 100644 assets/sdc-icons/components/radio_checked .svg create mode 100644 assets/sdc-icons/components/radio_disabled.svg create mode 100644 assets/sdc-icons/composition-o.svg create mode 100644 assets/sdc-icons/copy-o.svg create mode 100644 assets/sdc-icons/deployment-artifacts-o.svg create mode 100644 assets/sdc-icons/description-o.svg create mode 100644 assets/sdc-icons/distributed.svg create mode 100644 assets/sdc-icons/download-o.svg create mode 100644 assets/sdc-icons/edit-file-o.svg create mode 100644 assets/sdc-icons/edit-o.svg create mode 100644 assets/sdc-icons/expand-o.svg create mode 100644 assets/sdc-icons/eye-o.svg create mode 100644 assets/sdc-icons/filter-o.svg create mode 100644 assets/sdc-icons/info-circle-o.svg create mode 100644 assets/sdc-icons/info-circle.svg create mode 100644 assets/sdc-icons/info-square-o.svg create mode 100644 assets/sdc-icons/inputs-o.svg create mode 100644 assets/sdc-icons/locked.svg create mode 100644 assets/sdc-icons/minus-circle.svg create mode 100644 assets/sdc-icons/minus.svg create mode 100644 assets/sdc-icons/notifications-o.svg create mode 100644 assets/sdc-icons/plus-circle-o.svg create mode 100644 assets/sdc-icons/plus-circle.svg create mode 100644 assets/sdc-icons/plus.svg create mode 100644 assets/sdc-icons/profile-o.svg create mode 100644 assets/sdc-icons/profiles-o.svg create mode 100644 assets/sdc-icons/question-mark-circle-o.svg create mode 100644 assets/sdc-icons/question-mark-circle.svg create mode 100644 assets/sdc-icons/req-capabilities-o.svg create mode 100644 assets/sdc-icons/revert-o.svg create mode 100644 assets/sdc-icons/save-o.svg create mode 100644 assets/sdc-icons/search-o.svg create mode 100644 assets/sdc-icons/settings-o.svg create mode 100644 assets/sdc-icons/spinner.svg create mode 100644 assets/sdc-icons/success-circle-o.svg create mode 100644 assets/sdc-icons/success.svg create mode 100644 assets/sdc-icons/sync-o.svg create mode 100644 assets/sdc-icons/trash-o.svg create mode 100644 assets/sdc-icons/undo-o.svg create mode 100644 assets/sdc-icons/unlocked-o.svg create mode 100644 assets/sdc-icons/upload-o.svg create mode 100644 assets/sdc-icons/v-circle-o.svg create mode 100644 assets/sdc-icons/v-circle.svg create mode 100644 assets/sdc-icons/x-circle-o.svg create mode 100644 assets/sdc-icons/x-circle.svg create mode 100644 components/accordion/accordion-basic.html create mode 100644 components/accordion/accordion.scss create mode 100644 components/autocomplete/_autocomplete.scss create mode 100644 components/autocomplete/autocomlete-close.html create mode 100644 components/autocomplete/autocomplete-open.html create mode 100644 components/button/_button.scss create mode 100644 components/button/button-link-auto.html create mode 100644 components/button/button-link-disabled.html create mode 100644 components/button/button-link-extra-small.html create mode 100644 components/button/button-link-large.html create mode 100644 components/button/button-link-medium.html create mode 100644 components/button/button-link-small.html create mode 100644 components/button/button-link.html create mode 100644 components/button/button-primary-auto.html create mode 100644 components/button/button-primary-disabled.html create mode 100644 components/button/button-primary-extra-small.html create mode 100644 components/button/button-primary-large.html create mode 100644 components/button/button-primary-medium.html create mode 100644 components/button/button-primary-small.html create mode 100644 components/button/button-primary.html create mode 100644 components/button/button-secondary-auto.html create mode 100644 components/button/button-secondary-disabled.html create mode 100644 components/button/button-secondary-extra-small.html create mode 100644 components/button/button-secondary-large.html create mode 100644 components/button/button-secondary-medium.html create mode 100644 components/button/button-secondary-small.html create mode 100644 components/button/button-secondary.html create mode 100644 components/checkbox/_checkbox.scss create mode 100644 components/checkbox/checkbox-checked.html create mode 100644 components/checkbox/checkbox-disabled-checked.html create mode 100644 components/checkbox/checkbox-disabled.html create mode 100644 components/checkbox/checkbox-unchecked.html create mode 100644 components/checklist/_checklist.scss create mode 100644 components/checklist/checklist-with-checked-items.html create mode 100644 components/checklist/checklist-with-disabled-items.html create mode 100644 components/checklist/multi-levels-checklist.html create mode 100644 components/checklist/simple-checklist.html create mode 100644 components/dropdown/_dropdown.scss create mode 100644 components/dropdown/dropdown-disabled.html create mode 100644 components/dropdown/dropdown-groups.html create mode 100644 components/dropdown/dropdown-requiered.html create mode 100644 components/dropdown/dropdown.html create mode 100644 components/filter-bar/_filter-bar.scss create mode 100644 components/filter-bar/filter-bar-with-text.html create mode 100644 components/filter-bar/filter-bar.html create mode 100644 components/icon/_icon.scss create mode 100644 components/input/_input.scss create mode 100644 components/input/input-disabled.html create mode 100644 components/input/input-error.html create mode 100644 components/input/input-number.html create mode 100644 components/input/input-placeholder.html create mode 100644 components/input/input-required.html create mode 100644 components/input/input-view-only.html create mode 100644 components/input/input.html create mode 100644 components/menu/_menu.scss create mode 100644 components/menu/popup-menu.html create mode 100644 components/menu/relative-popup-menu.html create mode 100644 components/modal/_modal.scss create mode 100644 components/modal/alert-modal.html create mode 100644 components/modal/custom-modal.html create mode 100644 components/modal/error-modal.html create mode 100644 components/modal/standard-modal.html create mode 100644 components/notification/_notification.scss create mode 100644 components/notification/notification-info.html create mode 100644 components/notifications-container/_notifications-container.scss create mode 100644 components/panel/basic-panel.html create mode 100644 components/panel/panel.scss create mode 100644 components/radio/_radio.scss create mode 100644 components/radio/radio-checked.html create mode 100644 components/radio/radio-disabled-checked.html create mode 100644 components/radio/radio-disabled.html create mode 100644 components/radio/radio-unchecked.html create mode 100644 components/radioGroup/_radioGroup.scss create mode 100644 components/radioGroup/radio-group-disabled.html create mode 100644 components/radioGroup/radio-group-no-title.html create mode 100644 components/radioGroup/radio-group-value.html create mode 100644 components/radioGroup/radio-group.html create mode 100644 components/search-bar/_search-bar.scss create mode 100644 components/search-bar/search-bar-with-text.html create mode 100644 components/search-bar/search-bar.html create mode 100644 components/tabs/tabs-disabled.html create mode 100644 components/tabs/tabs-header.html create mode 100644 components/tabs/tabs-menu.html create mode 100644 components/tabs/tabs.scss create mode 100644 components/tag-cloud/_tag-cloud.scss create mode 100644 components/tag-cloud/disabled-list.html create mode 100644 components/tag-cloud/list-with-active-add-button.html create mode 100644 components/tag-cloud/list-with-some-read-only-items.html create mode 100644 components/tag-cloud/list-with-unique-error.html create mode 100644 components/tag-cloud/simple-list.html create mode 100644 components/tile/_tile.scss create mode 100644 components/tile/tile-without-footer.html create mode 100644 components/tile/vendor-tile.html create mode 100644 components/tile/vfc-tile.html create mode 100644 components/tile/vlm-tile.html create mode 100644 components/tile/vsp-tile.html create mode 100644 components/tooltip/_tooltip.scss create mode 100644 components/validation/_validation.scss create mode 100644 demo/.gitignore create mode 100644 demo/README.md create mode 100644 demo/assests/README.md create mode 100644 demo/assests/icons/empty.txt create mode 100644 demo/assests/icons/locked.svg create mode 100644 demo/assests/icons/plus.svg create mode 100644 demo/assests/icons/unlocked.svg create mode 100644 demo/assests/icons/vendor.svg create mode 100644 demo/assests/icons/vlm.svg create mode 100644 demo/assests/icons/vsp.svg create mode 100644 demo/assests/images/empty.txt create mode 100644 demo/components/button.html create mode 100644 demo/components/colors.html create mode 100644 demo/components/tiles-generic.html create mode 100644 demo/components/tiles.html create mode 100644 demo/index.css create mode 100644 demo/index.html create mode 100644 demo/index.js create mode 100644 demo/package.json create mode 100644 designs/README.md create mode 100644 jest.config.js create mode 100644 json-typing.d.ts create mode 100644 karma.conf.js create mode 100644 karma.entry.js create mode 100644 ng2-component-lab.webpack.config.js create mode 100644 package.json create mode 100644 pom.xml create mode 100644 rollup.angular.module.config.js create mode 100644 rollup.angular.umd.config.js create mode 100644 src/README.md create mode 100644 src/angular/accordion/accordion.component.html.ts create mode 100644 src/angular/accordion/accordion.component.ts create mode 100644 src/angular/accordion/accordion.module.ts create mode 100644 src/angular/animations/animation-directives.module.ts create mode 100644 src/angular/animations/ripple-click.animation.directive.ts create mode 100644 src/angular/autocomplete/autocomplete.component.html.ts create mode 100644 src/angular/autocomplete/autocomplete.component.ts create mode 100644 src/angular/autocomplete/autocomplete.module.ts create mode 100644 src/angular/autocomplete/autocomplete.pipe.ts create mode 100644 src/angular/buttons/button.component.html.ts create mode 100644 src/angular/buttons/button.component.ts create mode 100644 src/angular/buttons/buttons.module.ts create mode 100644 src/angular/checklist/checklist.component.html.ts create mode 100644 src/angular/checklist/checklist.component.ts create mode 100644 src/angular/checklist/checklist.module.ts create mode 100644 src/angular/checklist/models/Checklist.ts create mode 100644 src/angular/checklist/models/ChecklistItem.ts create mode 100644 src/angular/common/enums.ts create mode 100644 src/angular/common/index.ts create mode 100644 src/angular/components.ts create mode 100644 src/angular/filterbar/filter-bar.component.html.ts create mode 100644 src/angular/filterbar/filter-bar.component.ts create mode 100644 src/angular/filterbar/filter-bar.module.ts create mode 100644 src/angular/form-elements/checkbox/checkbox.component.html.ts create mode 100644 src/angular/form-elements/checkbox/checkbox.component.spec.ts create mode 100644 src/angular/form-elements/checkbox/checkbox.component.ts create mode 100644 src/angular/form-elements/dropdown/dropdown-models.ts create mode 100644 src/angular/form-elements/dropdown/dropdown-trigger.directive.ts create mode 100644 src/angular/form-elements/dropdown/dropdown.component.html.ts create mode 100644 src/angular/form-elements/dropdown/dropdown.component.spec.ts create mode 100644 src/angular/form-elements/dropdown/dropdown.component.ts create mode 100644 src/angular/form-elements/form-elements.module.ts create mode 100644 src/angular/form-elements/input/input.component.html.ts create mode 100644 src/angular/form-elements/input/input.component.ts create mode 100644 src/angular/form-elements/radios/radio-button.model.ts create mode 100644 src/angular/form-elements/radios/radio-buttons-group.component.html.ts create mode 100644 src/angular/form-elements/radios/radio-buttons-group.component.spec.ts create mode 100644 src/angular/form-elements/radios/radio-buttons-group.component.ts create mode 100644 src/angular/form-elements/validation/validatable.component.ts create mode 100644 src/angular/form-elements/validation/validatable.interface.ts create mode 100644 src/angular/form-elements/validation/validation-group.component.html.ts create mode 100644 src/angular/form-elements/validation/validation-group.component.ts create mode 100644 src/angular/form-elements/validation/validation.component.html.ts create mode 100644 src/angular/form-elements/validation/validation.component.ts create mode 100644 src/angular/form-elements/validation/validation.module.ts create mode 100644 src/angular/form-elements/validation/validators/base.validator.component.html.ts create mode 100644 src/angular/form-elements/validation/validators/base.validator.component.ts create mode 100644 src/angular/form-elements/validation/validators/custom.validator.component.ts create mode 100644 src/angular/form-elements/validation/validators/regex.validator.component.ts create mode 100644 src/angular/form-elements/validation/validators/required.validator.component.ts create mode 100644 src/angular/form-elements/validation/validators/validator.interface.ts create mode 100644 src/angular/index.ts create mode 100644 src/angular/infinite-scroll/infinite-scroll.directive.ts create mode 100644 src/angular/infinite-scroll/infinite-scroll.module.ts create mode 100644 src/angular/modals/modal-button.component.ts create mode 100644 src/angular/modals/modal-close-button.component.ts create mode 100644 src/angular/modals/modal.component.html.ts create mode 100644 src/angular/modals/modal.component.spec.ts create mode 100644 src/angular/modals/modal.component.ts create mode 100644 src/angular/modals/modal.module.ts create mode 100644 src/angular/modals/modal.service.ts create mode 100644 src/angular/modals/models/modal-config.ts create mode 100644 src/angular/ng1.module.ts create mode 100644 src/angular/notifications/container/notifcontainer.component.html.ts create mode 100644 src/angular/notifications/container/notifcontainer.component.ts create mode 100644 src/angular/notifications/notification-inner-content-example.component.ts create mode 100644 src/angular/notifications/notification.module.ts create mode 100644 src/angular/notifications/notification/notification.component.html.ts create mode 100644 src/angular/notifications/notification/notification.component.ts create mode 100644 src/angular/notifications/services/notifications.service.ts create mode 100644 src/angular/notifications/utilities/notification.config.ts create mode 100644 src/angular/popup-menu/popup-menu-item.component.spec.ts create mode 100644 src/angular/popup-menu/popup-menu-item.component.ts create mode 100644 src/angular/popup-menu/popup-menu-list.component.ts create mode 100644 src/angular/popup-menu/popup-menu.module.ts create mode 100644 src/angular/searchbar/search-bar.component.html.ts create mode 100644 src/angular/searchbar/search-bar.component.ts create mode 100644 src/angular/searchbar/search-bar.module.ts create mode 100644 src/angular/svg-icon/svg-icon-label.component.html.ts create mode 100644 src/angular/svg-icon/svg-icon-label.component.ts create mode 100644 src/angular/svg-icon/svg-icon.component.html.ts create mode 100644 src/angular/svg-icon/svg-icon.component.ts create mode 100644 src/angular/svg-icon/svg-icon.module.ts create mode 100644 src/angular/tabs/children/tab.component.html.ts create mode 100644 src/angular/tabs/children/tab.component.ts create mode 100644 src/angular/tabs/tabs.component.html.ts create mode 100644 src/angular/tabs/tabs.component.ts create mode 100644 src/angular/tabs/tabs.module.ts create mode 100644 src/angular/tag-cloud/tag-cloud.component.html.ts create mode 100644 src/angular/tag-cloud/tag-cloud.component.ts create mode 100644 src/angular/tag-cloud/tag-cloud.module.ts create mode 100644 src/angular/tag-cloud/tag-item/tag-item.component.html.ts create mode 100644 src/angular/tag-cloud/tag-item/tag-item.component.ts create mode 100644 src/angular/tiles/children/tile-content.component.ts create mode 100644 src/angular/tiles/children/tile-footer.component.ts create mode 100644 src/angular/tiles/children/tile-header.component.ts create mode 100644 src/angular/tiles/tile.component.html.ts create mode 100644 src/angular/tiles/tile.component.ts create mode 100644 src/angular/tiles/tile.module.ts create mode 100644 src/angular/tooltip/tooltip-template.component.ts create mode 100644 src/angular/tooltip/tooltip.directive.ts create mode 100644 src/angular/tooltip/tooltip.module.ts create mode 100644 src/angular/utils/create-dynamic-component.service.ts create mode 100644 src/react/Accordion.js create mode 100644 src/react/Button.js create mode 100644 src/react/Checkbox.js create mode 100644 src/react/Checklist.js create mode 100644 src/react/Input.js create mode 100644 src/react/Modal.js create mode 100644 src/react/ModalBody.js create mode 100644 src/react/ModalFooter.js create mode 100644 src/react/ModalHeader.js create mode 100644 src/react/ModalTitle.js create mode 100644 src/react/Panel.js create mode 100644 src/react/PopupMenu.js create mode 100644 src/react/PopupMenuItem.js create mode 100644 src/react/Portal.js create mode 100644 src/react/Radio.js create mode 100644 src/react/RadioGroup.js create mode 100644 src/react/SVGIcon.js create mode 100644 src/react/Tab.js create mode 100644 src/react/TabPane.js create mode 100644 src/react/Tabs.js create mode 100644 src/react/Tile.js create mode 100644 src/react/TileFooter.js create mode 100644 src/react/TileFooterCell.js create mode 100644 src/react/TileInfo.js create mode 100644 src/react/TileInfoLine.js create mode 100644 src/react/index.js create mode 100644 src/style/scss/_common.scss create mode 100644 src/style/scss/_components.scss create mode 100644 src/style/scss/angular/_svg_icon.scss create mode 100644 src/style/scss/angular/_tooltip_custom_style.scss create mode 100644 src/style/scss/common/_animation.scss create mode 100644 src/style/scss/common/_icons.scss create mode 100644 src/style/scss/common/_normalize.scss create mode 100644 src/style/scss/common/_typography.scss create mode 100644 src/style/scss/common/base.scss create mode 100644 src/style/scss/common/mixins.scss create mode 100644 src/style/scss/common/variables.scss create mode 100644 src/style/scss/style.scss create mode 100644 src/style/scss/themes/1802/_components.scss create mode 100644 src/style/scss/themes/1802/button.scss create mode 100644 src/style/scss/themes/1802/modal.scss create mode 100644 src/style/scss/themes/1802/style.scss create mode 100644 src/style/scss/themes/1802/tabs.scss create mode 100644 stories/README.md create mode 100644 stories/ng2-component-lab/accordion.component.exp.ts create mode 100644 stories/ng2-component-lab/autocomplete.component.exp.ts create mode 100644 stories/ng2-component-lab/button.component.exp.ts create mode 100644 stories/ng2-component-lab/checkbox.component.exp.ts create mode 100644 stories/ng2-component-lab/checklist.component.exp.ts create mode 100644 stories/ng2-component-lab/colors.component.exp.ts create mode 100644 stories/ng2-component-lab/components.module.ts create mode 100644 stories/ng2-component-lab/components/colors-table.component.ts create mode 100644 stories/ng2-component-lab/components/modal-consumer.component.ts create mode 100644 stories/ng2-component-lab/components/modal-inner-content-example.component.ts create mode 100644 stories/ng2-component-lab/components/notifications-example.component.ts create mode 100644 stories/ng2-component-lab/components/svg-icons-table.component.ts create mode 100644 stories/ng2-component-lab/dropdown.component.exp.ts create mode 100644 stories/ng2-component-lab/filter-bar.component.exp.ts create mode 100644 stories/ng2-component-lab/infinite-scroll.component.exp.ts create mode 100644 stories/ng2-component-lab/input.component.exp.ts create mode 100644 stories/ng2-component-lab/modals.component.exp.ts create mode 100644 stories/ng2-component-lab/notification.component.exp.ts create mode 100644 stories/ng2-component-lab/pipes/search-filter-pipe.ts create mode 100644 stories/ng2-component-lab/popup-menu.component.exp.ts create mode 100644 stories/ng2-component-lab/radio.component.exp.ts create mode 100644 stories/ng2-component-lab/search-bar.component.exp.ts create mode 100644 stories/ng2-component-lab/svg-icon.component.exp.ts create mode 100644 stories/ng2-component-lab/tabs.component.exp.ts create mode 100644 stories/ng2-component-lab/tag-cloud.component.exp.ts create mode 100644 stories/ng2-component-lab/tiles.component.exp.ts create mode 100644 stories/ng2-component-lab/tooltip.directive.exp.ts create mode 100644 stories/ng2-component-lab/utils/mock.json create mode 100644 stories/ng2-component-lab/utils/pipes/keys.pipe.ts create mode 100644 stories/ng2-component-lab/validation.component.exp.ts create mode 100644 stories/react/Accordion.stories.js create mode 100644 stories/react/Checkbox.stories.js create mode 100644 stories/react/Checklist.stories.js create mode 100644 stories/react/Colors.stories.js create mode 100644 stories/react/Input.stories.js create mode 100644 stories/react/Modal.stories.js create mode 100644 stories/react/Panel.stories.js create mode 100644 stories/react/PopupMenu.stories.js create mode 100644 stories/react/Radio.stories.js create mode 100644 stories/react/RadioGroup.stories.js create mode 100644 stories/react/SVGIcon.stories.js create mode 100644 stories/react/Tabs.stories.js create mode 100644 stories/react/Tiles.stories.js create mode 100644 stories/react/Typography.stories.js create mode 100644 stories/react/buttons/LinkButtons.stories.js create mode 100644 stories/react/buttons/PrimaryButtons.stories.js create mode 100644 stories/react/buttons/SecondaryButtons.stories.js create mode 100644 stories/react/index.js create mode 100644 stories/react/utils/BeautifyHTML.js create mode 100644 stories/react/utils/Examples.js create mode 100644 stories/react/utils/InsertSVGIcons.js create mode 100644 stories/react/utils/SourceToggle.js create mode 100644 stories/react/utils/components/DropdownMenu.js create mode 100644 stories/react/utils/jsxToString.js create mode 100644 test/react/Accordion.spec.js create mode 100644 test/react/Button.spec.js create mode 100644 test/react/Checkbox.spec.js create mode 100644 test/react/Checklist.spec.js create mode 100644 test/react/Input.spec.js create mode 100644 test/react/Modal.spec.js create mode 100644 test/react/ModalBody.spec.js create mode 100644 test/react/ModalFooter.spec.js create mode 100644 test/react/ModalHeader.spec.js create mode 100644 test/react/ModalTitle.spec.js create mode 100644 test/react/Panel.spec.js create mode 100644 test/react/PopupMenu.spec.js create mode 100644 test/react/PopupMenuItem.spec.js create mode 100644 test/react/Portal.spec.js create mode 100644 test/react/Radio.spec.js create mode 100644 test/react/RadioGroup.spec.js create mode 100644 test/react/Tabs.spec.js create mode 100644 test/react/Tile.spec.js create mode 100644 test/react/__snapshots__/Accordion.spec.js.snap create mode 100644 test/react/__snapshots__/Button.spec.js.snap create mode 100644 test/react/__snapshots__/Checkbox.spec.js.snap create mode 100644 test/react/__snapshots__/Checklist.spec.js.snap create mode 100644 test/react/__snapshots__/Input.spec.js.snap create mode 100644 test/react/__snapshots__/Modal.spec.js.snap create mode 100644 test/react/__snapshots__/ModalBody.spec.js.snap create mode 100644 test/react/__snapshots__/ModalFooter.spec.js.snap create mode 100644 test/react/__snapshots__/ModalHeader.spec.js.snap create mode 100644 test/react/__snapshots__/ModalTitle.spec.js.snap create mode 100644 test/react/__snapshots__/Panel.spec.js.snap create mode 100644 test/react/__snapshots__/PopupMenu.spec.js.snap create mode 100644 test/react/__snapshots__/PopupMenuItem.spec.js.snap create mode 100644 test/react/__snapshots__/Radio.spec.js.snap create mode 100644 test/react/__snapshots__/RadioGroup.spec.js.snap create mode 100644 test/react/__snapshots__/Tabs.spec.js.snap create mode 100644 test/react/__snapshots__/Tile.spec.js.snap create mode 100644 test/react/utils/htmlLoader.js create mode 100644 test/react/utils/svgMock.js create mode 100644 tsconfig.angular.build-es5.json create mode 100644 tsconfig.json create mode 100644 tslint.json create mode 100644 utils/build-demo.js create mode 100644 utils/create-icon-map.js create mode 100644 utils/create-svg-icons-map.js create mode 100644 utils/index-for-gh-pages.html create mode 100644 utils/main-page.html create mode 100644 utils/scripts/map-sources.js create mode 100644 version.properties create mode 100644 webpack.build.config.js create mode 100644 webpack/helpers.js create mode 100644 webpack/webpack.test.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..a4105e9 --- /dev/null +++ b/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["react", "env"], + "plugins": ["transform-object-rest-spread"] +} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..5ba69e2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +# tab indentation +[framework/**.js] +[framework/**.jsx] +[resources/**.scss] +indent_style = space +indent_size = 4 diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..5a7f317 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,135 @@ +{ + "parser": "babel-eslint", + "env": { + "es6": true, + "node": true, + "jest": true + }, + "plugins": [ + "react", + "import" + ], + "ecmaFeatures": { + "jsx": true, + "classes": true, + "modules": true + }, + "globals": { + "ICON_PATH": true, + "ICON_NAMES": true + }, + "rules": { + "linebreak-style": 0, + "no-unused-vars": 2, + "no-bitwise": 0, + "no-eq-null": 2, + "eqeqeq": 2, + "wrap-iife": [ + 2, + "any" + ], + "no-unused-expressions": 2, + "indent": [ + 1, + "tab", + { + "SwitchCase": 1 + } + ], + "no-use-before-define": 2, + "new-cap": [ + 2, + { + "capIsNewExceptions": [ + "DataTable", + "V" + ] + } + ], + "no-caller": 2, + "no-empty": 2, + "no-undef": 2, + "quotes": [ + 2, + "single", + "avoid-escape" + ], + "jsx-quotes": [ + 2, + "prefer-single" + ], + "no-plusplus": 0, + "no-cond-assign": [ + 2, + "except-parens" + ], + "comma-style": [ + 2, + "last" + ], + "no-invalid-this": 0, + "dot-notation": 0, + "max-len": [ + 1, + 200 + ], + "camelcase": [ + 2, + { + "properties": "never" + } + ], + "curly": 2, + "brace-style": 0, + "semi": [ + 2, + "always" + ], + "space-in-brackets": [ + 0, + "never" + ], + "space-infix-ops": 2, + "import/default": 0, + "import/no-unresolved": 0, + "import/no-named-as-default": 2, + "import/no-duplicates": 0, + "import/imports-first": 2, + "import/export": 2, + "react/display-name": 0, + "react/forbid-prop-types": 0, + "react/jsx-boolean-value": 0, + "react/jsx-closing-bracket-location": [ + 1, + { + "nonEmpty": "after-props", + "selfClosing": "after-props" + } + ], + "react/jsx-curly-spacing": 0, + "react/jsx-indent-props": [ + 1, + "tab" + ], + "react/jsx-max-props-per-line": 0, + "react/jsx-no-duplicate-props": 1, + "react/jsx-no-literals": 0, + "react/jsx-no-undef": 1, + "react/jsx-sort-prop-types": 0, + "react/jsx-sort-props": 0, + "react/jsx-uses-react": 1, + "react/jsx-uses-vars": 1, + "react/no-danger": 1, + "react/no-did-mount-set-state": 2, + "react/no-did-update-set-state": 2, + "react/no-direct-mutation-state": 1, + "react/no-multi-comp": 0, + "react/no-set-state": 0, + "react/no-unknown-property": 1, + "react/prop-types": 0, + "react/react-in-jsx-scope": 1, + "react/self-closing-comp": 1, + "react/sort-comp": 0, + "react/jsx-wrap-multilines": 1 + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..75d10d0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +/dist +/tmp +/lib +/build +/node_modules +npm-debug.log* +Thumbs.db + +# IDEA files +.idea +.vscode +.history +package-lock.json +# build files +lib +css +demo/gen +.out +src/react/utils/iconMap.js + +#coverage files +coverage + +#ignore generated icons files +src/common/icons-map.ts +src/common/icons-map.json + +/node/* +/yarn.lock diff --git a/.gitreview b/.gitreview new file mode 100644 index 0000000..50cc496 --- /dev/null +++ b/.gitreview @@ -0,0 +1,4 @@ +[gerrit] +host=gerrit.onap.org +port=29418 +project=sdc/onap-ui.git diff --git a/.ng2-component-lab/lab-configuration.module.ts b/.ng2-component-lab/lab-configuration.module.ts new file mode 100644 index 0000000..d9305dd --- /dev/null +++ b/.ng2-component-lab/lab-configuration.module.ts @@ -0,0 +1,36 @@ +import { createLab } from '@islavi/ng2-component-lab'; +import { ComponentsModule } from './../stories/ng2-component-lab/components.module'; + +const themeName:string = 'default'; +//const themeName:string = '1802'; + +// Select the theme +if (themeName === '1802') { + require('./themes/ng2-component-lab-theme-1802.scss'); +} else { + // Default theme + require('./ng2-component-lab.scss'); +} + +createLab({ + /** + * NgModule to import. All components and pipes must be exported + * by this module to be useable in your experiments + */ + ngModule: ComponentsModule, + /** + * Function that returns an array of experiments. + * + * Here is an example using webpack's `require.context` to + * load all modules ending in `.exp.ts` and returning thier + * default exports as an array: + */ + loadExperiments() { + const context = (require as any).context('./../stories/ng2-component-lab', true, /\.exp\.ts/); + var result = context.keys().map(context).map(mod => mod.default); + context.keys().forEach(key => { + console.log("Going to require: " + key); + }); + return result; + } +}); diff --git a/.ng2-component-lab/ng2-component-lab.config.js b/.ng2-component-lab/ng2-component-lab.config.js new file mode 100644 index 0000000..963ac45 --- /dev/null +++ b/.ng2-component-lab/ng2-component-lab.config.js @@ -0,0 +1,11 @@ +var getWebPackConfig = require('../ng2-component-lab.webpack.config.js'); + +module.exports = { + webpackConfig: getWebPackConfig, + host: 'localhost', + port: 6007, + include: [], + suites: { + feature: './.ng2-component-lab/lab-configuration.module.ts' + } +}; diff --git a/.ng2-component-lab/ng2-component-lab.scss b/.ng2-component-lab/ng2-component-lab.scss new file mode 100644 index 0000000..28f0f7b --- /dev/null +++ b/.ng2-component-lab/ng2-component-lab.scss @@ -0,0 +1,58 @@ +@font-face { + font-family: 'OpenSans-Regular'; + font-style: normal; + font-weight: 400; + src: + local('Open Sans Regular'), + local('OpenSans-Regular'), + url(https://fonts.gstatic.com/s/opensans/v15/cJZKeOuBrn4kERxqtaUH3ZBw1xU1rKptJj_0jans920.woff2) format('woff2'), + url(http://fonts.gstatic.com/s/opensans/v15/cJZKeOuBrn4kERxqtaUH3T8E0i7KZn-EPnyo3HZu7kw.woff) format('woff'); +} +@font-face { + font-family: 'OpenSans-Italic'; + font-style: normal; + font-weight: 400; + src: + local('Open Sans Italic'), + local('OpenSans-Italic'), + url(https://fonts.gstatic.com/s/opensans/v15/xjAJXh38I15wypJXxuGMBogp9Q8gbYrhqGlRav_IXfk.woff2) format('woff2'), + url(http://fonts.gstatic.com/s/opensans/v15/xjAJXh38I15wypJXxuGMBobN6UDyHWBl620a-IRfuBk.woff) format('woff'); +} +@font-face { + font-family: 'OpenSans-Semibold'; + font-style: normal; + font-weight: 400; + src: + local('Open Sans Semibold'), + local('OpenSans-Semibold'), + url(https://fonts.gstatic.com/s/opensans/v15/MTP_ySUJH_bn48VBG8sNShampu5_7CjHW5spxoeN3Vs.woff2) format('woff2'), + url(http://fonts.gstatic.com/s/opensans/v15/MTP_ySUJH_bn48VBG8sNSnhCUOGz7vYGh680lGh-uXM.woff) format('woff'); +} + +@import '../src/style/scss/style.scss'; + +.colors-table { + width: 75%; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + font-size: 13px; + .color-section { + display: flex; + flex-direction: column; + margin: 10px 0; + min-width: 150px; + div { + align-self: center; + user-select: text; + } + $circle-size: 40px; + .color-circle { + height: $circle-size; + width: $circle-size; + border-radius: $circle-size; + padding: 10px; + text-align: center; + } + } +} diff --git a/.ng2-component-lab/themes/ng2-component-lab-theme-1802.scss b/.ng2-component-lab/themes/ng2-component-lab-theme-1802.scss new file mode 100644 index 0000000..59cf05e --- /dev/null +++ b/.ng2-component-lab/themes/ng2-component-lab-theme-1802.scss @@ -0,0 +1,58 @@ +@font-face { + font-family: 'OpenSans-Regular'; + font-style: normal; + font-weight: 400; + src: + local('Open Sans Regular'), + local('OpenSans-Regular'), + url(https://fonts.gstatic.com/s/opensans/v15/cJZKeOuBrn4kERxqtaUH3ZBw1xU1rKptJj_0jans920.woff2) format('woff2'), + url(http://fonts.gstatic.com/s/opensans/v15/cJZKeOuBrn4kERxqtaUH3T8E0i7KZn-EPnyo3HZu7kw.woff) format('woff'); +} +@font-face { + font-family: 'OpenSans-Italic'; + font-style: normal; + font-weight: 400; + src: + local('Open Sans Italic'), + local('OpenSans-Italic'), + url(https://fonts.gstatic.com/s/opensans/v15/xjAJXh38I15wypJXxuGMBogp9Q8gbYrhqGlRav_IXfk.woff2) format('woff2'), + url(http://fonts.gstatic.com/s/opensans/v15/xjAJXh38I15wypJXxuGMBobN6UDyHWBl620a-IRfuBk.woff) format('woff'); +} +@font-face { + font-family: 'OpenSans-Semibold'; + font-style: normal; + font-weight: 400; + src: + local('Open Sans Semibold'), + local('OpenSans-Semibold'), + url(https://fonts.gstatic.com/s/opensans/v15/MTP_ySUJH_bn48VBG8sNShampu5_7CjHW5spxoeN3Vs.woff2) format('woff2'), + url(http://fonts.gstatic.com/s/opensans/v15/MTP_ySUJH_bn48VBG8sNSnhCUOGz7vYGh680lGh-uXM.woff) format('woff'); +} + +@import './../../src/style/scss/themes/1802/style.scss'; + +.colors-table { + width: 75%; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + font-size: 13px; + .color-section { + display: flex; + flex-direction: column; + margin: 10px 0; + min-width: 150px; + div { + align-self: center; + user-select: text; + } + $circle-size: 40px; + .color-circle { + height: $circle-size; + width: $circle-size; + border-radius: $circle-size; + padding: 10px; + text-align: center; + } + } +} diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..7d482d2 --- /dev/null +++ b/.npmignore @@ -0,0 +1,6 @@ +/dist + +# IDEA files +.idea +.vscode +.history diff --git a/.storybook/config.js b/.storybook/config.js new file mode 100644 index 0000000..5896dcc --- /dev/null +++ b/.storybook/config.js @@ -0,0 +1,8 @@ +import { configure } from '@storybook/react'; +import './storybook.scss'; + +function loadStories() { + require('../stories/react'); +} + +configure(loadStories, module); diff --git a/.storybook/storybook.scss b/.storybook/storybook.scss new file mode 100644 index 0000000..ce905cf --- /dev/null +++ b/.storybook/storybook.scss @@ -0,0 +1,231 @@ +@import 'typography.scss'; +@import '../src/style/scss/style.scss'; +@import '~prismjs/themes/prism.css'; + +body { + @include base-font-regular(); + margin: 15px; + max-width: 800px; +} + +a { + color: #1474f3; + text-decoration: none; + border-bottom: 1px solid #1474f3; + padding-bottom: 2px; +} + +.colors-table { + width: 100%; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + font-size: 13px; + .color-section { + display: flex; + flex-direction: column; + margin: 10px 0; + min-width: 150px; + div { + align-self: center; + user-select: text; + } + $circle-size: 40px; + .color-circle { + height: $circle-size; + width: $circle-size; + border-radius: $circle-size; + padding: 10px; + text-align: center; + } + } +} + +.icons-screen { + width: 1190px; + .missing-icon-section { + border-top: 1px solid $light-gray; + padding-top: 10px; + } + .icons-option-selector { + margin-top: 20px; + margin-bottom: 20px; + display: flex; + .option-container { + display: flex; + flex-direction: column; + flex-basis: 100%; + margin-right: 20px; + } + } + + .icons-table { + margin-top: 50px; + width: 100%; + display: flex; + flex-wrap: wrap; + border-top: 1px solid $light-gray; + justify-content: flex-start; + .icon-section { + display: flex; + flex-direction: column; + margin: 10px; + min-width: 150px; + .svg-icon-wrapper { + cursor: pointer; + width: 20px; + height: 42px; + margin-left: auto; + margin-right: auto; + .svg-icon-label { + font-size: 12px; + } + } + } + } + + .svg-icon { + &.storybook-big { + width: 120px; + height: 120px; + } + &.storybook-small { + width: 60px; + height: 60px; + } + } +} + +.sdc-popup-menu { + @include box-shadow(0 1px 3px 0 rgba(0, 0, 0, 0.2)); + width: 200px; + height: 200px; + border: 1px solid $light-gray; +} + +.typography-screen { + .typography-section { + margin: 20px 0; + } + ul { + font-size: 18px; + } + .typo-table { + .typo-section { + div { + display: inline-block; + //border: 1px solid black; + width: 30%; + margin: 4px 0; + } + .sample-text { + white-space: nowrap; + + } + } + } + .heading-1 { + @include heading-1; + } + .heading-2 { + @include heading-2; + } + .heading-3 { + @include heading-3; + } + .heading-4 { + @include heading-4; + } + .heading-4-emphasis { + @include heading-4-emphasis; + } + .heading-5 { + @include heading-5; + } + + .body-1 { + @include body-1; + } + + .body-1-italic { + @include body-1-italic; + } + + .body-2 { + @include body-2; + } + + .body-2-emphasis { + @include body-2-emphasis; + } + + .body-3 { + @include body-3; + } + + .body-3-emphasis { + @include body-3-emphasis; + } + + .body-4 { + @include body-4; + } +} + +.source-toggle-wrapper { + .source-toggle-title { + @include heading-2; + } + .source-toggle { + display: flex; + border-top: 1px solid $light-gray; + padding: 10px 0; + &:first-child { + border-top: none; + } + .source-toggle-example { + flex-basis: 40%; + flex-shrink: 0; + } + .source-toggle-code { + background-color: $light-silver; + padding: 5px; + margin-left: 30px; + pre { + margin-top: 0; + user-select: text; + overflow-x: auto; + overflow-y: hidden; + max-width: 800px; + code { + font-size: 15px; + font-weight: 600; + padding: 2px 5px; + border: 1px solid #eae9e9; + border-radius: 4px; + background-color: #f3f2f2; + color: #3a3a3a; + .token { + line-height: 20px; + } + } + } + .source-toggle-code-tabs { + display: flex; + margin-bottom: 10px; + .source-toggle-tab { + @include body-1; + @include base-font-semibold; + margin-right: 20px; + cursor: pointer; + &.selected { + border-bottom: 2px solid $dark-blue; + color: $dark-blue; + font-weight: bold; + } + } + } + } + } +} + diff --git a/.storybook/typography.scss b/.storybook/typography.scss new file mode 100644 index 0000000..36f9b02 --- /dev/null +++ b/.storybook/typography.scss @@ -0,0 +1,30 @@ +@font-face { + font-family: 'OpenSans-Regular'; + font-style: normal; + font-weight: 400; + src: + local('Open Sans Regular'), + local('OpenSans-Regular'), + url(https://fonts.gstatic.com/s/opensans/v15/cJZKeOuBrn4kERxqtaUH3ZBw1xU1rKptJj_0jans920.woff2) format('woff2'), + url(http://fonts.gstatic.com/s/opensans/v15/cJZKeOuBrn4kERxqtaUH3T8E0i7KZn-EPnyo3HZu7kw.woff) format('woff'); +} +@font-face { + font-family: 'OpenSans-Italic'; + font-style: normal; + font-weight: 400; + src: + local('Open Sans Italic'), + local('OpenSans-Italic'), + url(https://fonts.gstatic.com/s/opensans/v15/xjAJXh38I15wypJXxuGMBogp9Q8gbYrhqGlRav_IXfk.woff2) format('woff2'), + url(http://fonts.gstatic.com/s/opensans/v15/xjAJXh38I15wypJXxuGMBobN6UDyHWBl620a-IRfuBk.woff) format('woff'); +} +@font-face { + font-family: 'OpenSans-Semibold'; + font-style: normal; + font-weight: 400; + src: + local('Open Sans Semibold'), + local('OpenSans-Semibold'), + url(https://fonts.gstatic.com/s/opensans/v15/MTP_ySUJH_bn48VBG8sNShampu5_7CjHW5spxoeN3Vs.woff2) format('woff2'), + url(http://fonts.gstatic.com/s/opensans/v15/MTP_ySUJH_bn48VBG8sNSnhCUOGz7vYGh680lGh-uXM.woff) format('woff'); +} diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js new file mode 100644 index 0000000..a923d80 --- /dev/null +++ b/.storybook/webpack.config.js @@ -0,0 +1,39 @@ + +const path = require('path'); +const webpack = require('webpack'); +const svgFolder = './assets/icons/'; +const fs = require('fs'); + +let iconNames = []; + +fs.readdirSync(svgFolder).forEach(file => { + let fileName = file.split('.'); + if (fileName[0] && fileName[1] === 'svg') { + iconNames.push(fileName[0]); + } +}); + +module.exports = { + module: { + rules: [ + { + test: /.scss$/, + use: ['style-loader', 'css-loader', 'sass-loader'], + include: path.resolve(__dirname, '../') + }, + { + test: /.html$/, + loader: 'html-loader', + options: { + minimize: false + } + } + ] + }, + plugins: [ + new webpack.DefinePlugin({ + 'ICON_PATH': '"./"', + 'ICON_NAMES':JSON.stringify(iconNames) + }) + ] +}; diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..3afac06 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,31 @@ +language: node_js +node_js: +- 6 +install: npm install +script: +- npm run build-common +- npm test +- npm run build +- npm run build-gh-pages +deploy: + - provider: npm + email: onap.sdc@gmail.com + api_key: $NPM_TOKEN + skip_cleanup: true + on: + tags: true + repo: onap-sdc/sdc-ui + - provider: pages + repo: onap-sdc/sdc-ui + skip_cleanup: true + github_token: $GITHUB_TOKEN + local_dir: .out + on: + branch: master + - provider: pages + skip_cleanup: true + github_token: $GITHUB_TOKEN + local_dir: .out + on: + all_branches: true + condition: $DEPLOY = 1 diff --git a/LICENSE.TXT b/LICENSE.TXT new file mode 100644 index 0000000..f2eb9ea --- /dev/null +++ b/LICENSE.TXT @@ -0,0 +1,20 @@ +/* +* ============LICENSE_START========================================== +* =================================================================== +* Copyright © 2018 AT&T Intellectual Property. +* Copyright © 2018 Amdocs +* All rights reserved. +* =================================================================== +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* ============LICENSE_END============================================ +*/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e83552b --- /dev/null +++ b/README.md @@ -0,0 +1,111 @@ +# SDC-UI Style-Guide and Components + +This project aims to create a unified UI styled components for multiple development teams who work on the same web-based applications. +This repository contains the definition of all the basic widgets and reusable controllers. + + +### Usage + +#### Link the library's CSS file +There are several options to link to sdc-ui CSS file: + +###### SCSS +```scss +@import "path_to_node_modules/sdc-ui/css/style.css"; +``` +###### HTML +```html + +``` +###### As Module (Using loading tool, i.e. [Webpack](https://webpack.github.io/)) +```js +import 'sdc-ui/css/style.css'; +``` +###### Angular CLI projects +You can add this line to style.css file: +```js +@import "../node_modules/sdc-ui/css/style.css"; +``` + +#### React Code examples +###### Importing particular component +```js +import Button from 'sdc-ui/lib/react/Button.js'; + +// inside component rendering... +render(){ + return ( + + ); +} +``` +###### Importing particular component from the react library +```js +import {Button} from 'sdc-ui/lib/react'; + +// inside component rendering... +render(){ + return ( + + ); +} +``` +###### Importing the entire library +```js +import SDCUI from 'sdc-ui'; + +// inside component rendering... +render(){ + return ( + I am still a Button + ); +} +``` + +#### Using the library in Angular (2-5) +###### Add the library to your module +```js + import { SdcUiComponentsModule, SdcUiComponents } from 'sdc-ui/lib/angular'; + + @NgModule({ + declarations: [ + AppComponent + ], + imports: [ + BrowserModule, + FormsModule, + HttpModule, + SdcUiComponentsModule + ], + providers: [ + SdcUiComponents.ModalService + ], + bootstrap: [AppComponent] + }) + export class AppModule { } +``` + + +### Running storybook +The components in this library are displayed via [storybook](https://github.com/storybooks/storybook). Head to [http://onap-sdc.github.io/sdc-ui](http://onap-sdc.github.io/sdc-ui) to see the components that are in `master`. + +While developing, just run `npm run storybook` in your terminal to launch a local storybook server where you can see your changes. For deploying storybook to your own fork repository, refer to the guides section below. + + +### Running component-lab +To see angular components in design run: npm run lab + + +### Useful guides +[Adding a new component](https://github.com/onap-sdc/sdc-ui/wiki/Adding-a-new-component) + +[Deploying storybook to a fork's github pages](https://github.com/onap-sdc/sdc-ui/wiki/Deploying-storybook-to-a-fork's-github-pages) + +### Having some trouble? Have an issue? +For bugs and issues, please use the [issues](https://github.com/onap-sdc/sdc-ui/issues) page + +### How to Contribute +**Contribution can be made only by following these guide lines** +* This project combines both `React` & `Angular` framework libraries. Hence, every change in the basic HTML files structure, must be followed by changes on the frameworks files accordingly (under `src/react` and `src/angular`). +* There will be no any 3rd party UI framework imported (i.e. `Bootstrap`, `Material`, `Foundation`... etc.). +* Contribution are done only by the [contribution guide](https://github.com/onap-sdc/sdc-ui/wiki/Contribution-guide). Contributions submitted not in this format and guidelines will not be considered. diff --git a/assets/README.md b/assets/README.md new file mode 100644 index 0000000..5f2681b --- /dev/null +++ b/assets/README.md @@ -0,0 +1,9 @@ + +# Folder Structure + +### icons +Contains `svg` icons **ONLY** + + +### images +Contains large images (if any...) diff --git a/assets/icons/angleDoubleLeft.svg b/assets/icons/angleDoubleLeft.svg new file mode 100644 index 0000000..9e1591a --- /dev/null +++ b/assets/icons/angleDoubleLeft.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/assets/icons/angleDoubleRight.svg b/assets/icons/angleDoubleRight.svg new file mode 100644 index 0000000..e77031a --- /dev/null +++ b/assets/icons/angleDoubleRight.svg @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/assets/icons/angleLeft.svg b/assets/icons/angleLeft.svg new file mode 100644 index 0000000..b2d2f81 --- /dev/null +++ b/assets/icons/angleLeft.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/assets/icons/angleRight.svg b/assets/icons/angleRight.svg new file mode 100644 index 0000000..f8e6efc --- /dev/null +++ b/assets/icons/angleRight.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/assets/icons/artifacts.svg b/assets/icons/artifacts.svg new file mode 100644 index 0000000..41e6c8e --- /dev/null +++ b/assets/icons/artifacts.svg @@ -0,0 +1 @@ +Asset 1 \ No newline at end of file diff --git a/assets/icons/back.svg b/assets/icons/back.svg new file mode 100644 index 0000000..287355f --- /dev/null +++ b/assets/icons/back.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/assets/icons/base.svg b/assets/icons/base.svg new file mode 100644 index 0000000..89fbd43 --- /dev/null +++ b/assets/icons/base.svg @@ -0,0 +1 @@ +base.volume \ No newline at end of file diff --git a/assets/icons/calendar.svg b/assets/icons/calendar.svg new file mode 100644 index 0000000..9c05902 --- /dev/null +++ b/assets/icons/calendar.svg @@ -0,0 +1 @@ +Asset 1 \ No newline at end of file diff --git a/assets/icons/caretDown.svg b/assets/icons/caretDown.svg new file mode 100644 index 0000000..cfd3c57 --- /dev/null +++ b/assets/icons/caretDown.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/assets/icons/check.svg b/assets/icons/check.svg new file mode 100644 index 0000000..43d1881 --- /dev/null +++ b/assets/icons/check.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/assets/icons/checkCircle.svg b/assets/icons/checkCircle.svg new file mode 100644 index 0000000..313657e --- /dev/null +++ b/assets/icons/checkCircle.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/chevronDown.svg b/assets/icons/chevronDown.svg new file mode 100644 index 0000000..1ebd094 --- /dev/null +++ b/assets/icons/chevronDown.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/assets/icons/chevronUp.svg b/assets/icons/chevronUp.svg new file mode 100644 index 0000000..7fce935 --- /dev/null +++ b/assets/icons/chevronUp.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/assets/icons/close.svg b/assets/icons/close.svg new file mode 100644 index 0000000..0decc7c --- /dev/null +++ b/assets/icons/close.svg @@ -0,0 +1,10 @@ + + + + + + + diff --git a/assets/icons/download.svg b/assets/icons/download.svg new file mode 100644 index 0000000..dd64605 --- /dev/null +++ b/assets/icons/download.svg @@ -0,0 +1 @@ +Asset 2 \ No newline at end of file diff --git a/assets/icons/empty.txt b/assets/icons/empty.txt new file mode 100644 index 0000000..e69de29 diff --git a/assets/icons/env.svg b/assets/icons/env.svg new file mode 100644 index 0000000..ac68ae6 --- /dev/null +++ b/assets/icons/env.svg @@ -0,0 +1 @@ +env \ No newline at end of file diff --git a/assets/icons/error.svg b/assets/icons/error.svg new file mode 100644 index 0000000..bafb73b --- /dev/null +++ b/assets/icons/error.svg @@ -0,0 +1 @@ +error \ No newline at end of file diff --git a/assets/icons/errorCircle.svg b/assets/icons/errorCircle.svg new file mode 100644 index 0000000..8234753 --- /dev/null +++ b/assets/icons/errorCircle.svg @@ -0,0 +1 @@ +Asset 4 \ No newline at end of file diff --git a/assets/icons/exclamationTriangleFull.svg b/assets/icons/exclamationTriangleFull.svg new file mode 100644 index 0000000..7cab121 --- /dev/null +++ b/assets/icons/exclamationTriangleFull.svg @@ -0,0 +1,9 @@ + + + + + diff --git a/assets/icons/exclamationTriangleLine.svg b/assets/icons/exclamationTriangleLine.svg new file mode 100644 index 0000000..eae6825 --- /dev/null +++ b/assets/icons/exclamationTriangleLine.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/expand.svg b/assets/icons/expand.svg new file mode 100644 index 0000000..4095ef5 --- /dev/null +++ b/assets/icons/expand.svg @@ -0,0 +1 @@ +Asset 1 \ No newline at end of file diff --git a/assets/icons/filter.svg b/assets/icons/filter.svg new file mode 100644 index 0000000..1c493f4 --- /dev/null +++ b/assets/icons/filter.svg @@ -0,0 +1,9 @@ + + + + + diff --git a/assets/icons/locked.svg b/assets/icons/locked.svg new file mode 100644 index 0000000..ab9f0b9 --- /dev/null +++ b/assets/icons/locked.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/module.svg b/assets/icons/module.svg new file mode 100644 index 0000000..fa3901c --- /dev/null +++ b/assets/icons/module.svg @@ -0,0 +1 @@ +module \ No newline at end of file diff --git a/assets/icons/nestedHeat.svg b/assets/icons/nestedHeat.svg new file mode 100644 index 0000000..7e33068 --- /dev/null +++ b/assets/icons/nestedHeat.svg @@ -0,0 +1 @@ +nested_heat \ No newline at end of file diff --git a/assets/icons/network.svg b/assets/icons/network.svg new file mode 100644 index 0000000..018cd3f --- /dev/null +++ b/assets/icons/network.svg @@ -0,0 +1 @@ +network \ No newline at end of file diff --git a/assets/icons/notificationBell.svg b/assets/icons/notificationBell.svg new file mode 100644 index 0000000..6001d59 --- /dev/null +++ b/assets/icons/notificationBell.svg @@ -0,0 +1,18 @@ + + + +notification_icon + + + + + diff --git a/assets/icons/notificationFullBell.svg b/assets/icons/notificationFullBell.svg new file mode 100644 index 0000000..6e7e7cc --- /dev/null +++ b/assets/icons/notificationFullBell.svg @@ -0,0 +1,11 @@ + + + + + notification_full + + + + + + diff --git a/assets/icons/others.svg b/assets/icons/others.svg new file mode 100644 index 0000000..9a18e4a --- /dev/null +++ b/assets/icons/others.svg @@ -0,0 +1 @@ +others \ No newline at end of file diff --git a/assets/icons/pencil.svg b/assets/icons/pencil.svg new file mode 100644 index 0000000..6701a3a --- /dev/null +++ b/assets/icons/pencil.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/assets/icons/plus.svg b/assets/icons/plus.svg new file mode 100644 index 0000000..36f3486 --- /dev/null +++ b/assets/icons/plus.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/assets/icons/plusCircle.svg b/assets/icons/plusCircle.svg new file mode 100644 index 0000000..63781f5 --- /dev/null +++ b/assets/icons/plusCircle.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/icons/plusThin.svg b/assets/icons/plusThin.svg new file mode 100644 index 0000000..571c303 --- /dev/null +++ b/assets/icons/plusThin.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/proceedToOverview.svg b/assets/icons/proceedToOverview.svg new file mode 100644 index 0000000..4788106 --- /dev/null +++ b/assets/icons/proceedToOverview.svg @@ -0,0 +1 @@ +proceed_to_overview \ No newline at end of file diff --git a/assets/icons/questionMark.svg b/assets/icons/questionMark.svg new file mode 100644 index 0000000..3b1c7bc --- /dev/null +++ b/assets/icons/questionMark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/search.svg b/assets/icons/search.svg new file mode 100644 index 0000000..ce83104 --- /dev/null +++ b/assets/icons/search.svg @@ -0,0 +1,8 @@ + + + + + diff --git a/assets/icons/sliders.svg b/assets/icons/sliders.svg new file mode 100644 index 0000000..ade9de2 --- /dev/null +++ b/assets/icons/sliders.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + diff --git a/assets/icons/trashO.svg b/assets/icons/trashO.svg new file mode 100644 index 0000000..26336f1 --- /dev/null +++ b/assets/icons/trashO.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/assets/icons/unlocked.svg b/assets/icons/unlocked.svg new file mode 100644 index 0000000..1e341bd --- /dev/null +++ b/assets/icons/unlocked.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/upload.svg b/assets/icons/upload.svg new file mode 100644 index 0000000..72e64ff --- /dev/null +++ b/assets/icons/upload.svg @@ -0,0 +1 @@ +upload \ No newline at end of file diff --git a/assets/icons/user.svg b/assets/icons/user.svg new file mode 100644 index 0000000..3363f57 --- /dev/null +++ b/assets/icons/user.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/assets/icons/vendor.svg b/assets/icons/vendor.svg new file mode 100644 index 0000000..a3b8f5f --- /dev/null +++ b/assets/icons/vendor.svg @@ -0,0 +1 @@ +vendor diff --git a/assets/icons/versionControllerCommit.svg b/assets/icons/versionControllerCommit.svg new file mode 100644 index 0000000..5847c69 --- /dev/null +++ b/assets/icons/versionControllerCommit.svg @@ -0,0 +1 @@ +Asset 5 \ No newline at end of file diff --git a/assets/icons/versionControllerLockClosed.svg b/assets/icons/versionControllerLockClosed.svg new file mode 100644 index 0000000..73aae7d --- /dev/null +++ b/assets/icons/versionControllerLockClosed.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + diff --git a/assets/icons/versionControllerLockOpen.svg b/assets/icons/versionControllerLockOpen.svg new file mode 100644 index 0000000..52e0b8b --- /dev/null +++ b/assets/icons/versionControllerLockOpen.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + diff --git a/assets/icons/versionControllerPermissions.svg b/assets/icons/versionControllerPermissions.svg new file mode 100644 index 0000000..91ea9f4 --- /dev/null +++ b/assets/icons/versionControllerPermissions.svg @@ -0,0 +1 @@ +Asset 2 \ No newline at end of file diff --git a/assets/icons/versionControllerRevert.svg b/assets/icons/versionControllerRevert.svg new file mode 100644 index 0000000..f9f02f9 --- /dev/null +++ b/assets/icons/versionControllerRevert.svg @@ -0,0 +1,14 @@ + + + + + + + diff --git a/assets/icons/versionControllerSave.svg b/assets/icons/versionControllerSave.svg new file mode 100644 index 0000000..4a05425 --- /dev/null +++ b/assets/icons/versionControllerSave.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/assets/icons/versionControllerSubmit.svg b/assets/icons/versionControllerSubmit.svg new file mode 100644 index 0000000..9909ab3 --- /dev/null +++ b/assets/icons/versionControllerSubmit.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/assets/icons/versionControllerSync.svg b/assets/icons/versionControllerSync.svg new file mode 100644 index 0000000..dfd42d3 --- /dev/null +++ b/assets/icons/versionControllerSync.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/versionControllerUndo.svg b/assets/icons/versionControllerUndo.svg new file mode 100644 index 0000000..8745f54 --- /dev/null +++ b/assets/icons/versionControllerUndo.svg @@ -0,0 +1 @@ +Asset 3 \ No newline at end of file diff --git a/assets/icons/viewModule.svg b/assets/icons/viewModule.svg new file mode 100644 index 0000000..e121d40 --- /dev/null +++ b/assets/icons/viewModule.svg @@ -0,0 +1,9 @@ + + viewModule + + + + + + + diff --git a/assets/icons/vlm.svg b/assets/icons/vlm.svg new file mode 100644 index 0000000..79b4625 --- /dev/null +++ b/assets/icons/vlm.svg @@ -0,0 +1 @@ +vlm_new_icon \ No newline at end of file diff --git a/assets/icons/vsp.svg b/assets/icons/vsp.svg new file mode 100644 index 0000000..344755c --- /dev/null +++ b/assets/icons/vsp.svg @@ -0,0 +1 @@ +vsp_new_icon diff --git a/assets/icons/zip.svg b/assets/icons/zip.svg new file mode 100644 index 0000000..51ce7fa --- /dev/null +++ b/assets/icons/zip.svg @@ -0,0 +1 @@ +zip \ No newline at end of file diff --git a/assets/images/empty.txt b/assets/images/empty.txt new file mode 100644 index 0000000..e69de29 diff --git a/assets/images/illustration.png b/assets/images/illustration.png new file mode 100644 index 0000000..c0ff99a Binary files /dev/null and b/assets/images/illustration.png differ diff --git a/assets/images/logo_onap.png b/assets/images/logo_onap.png new file mode 100644 index 0000000..c6f6857 Binary files /dev/null and b/assets/images/logo_onap.png differ diff --git a/assets/images/logo_onap_2017.png b/assets/images/logo_onap_2017.png new file mode 100644 index 0000000..c6f6857 Binary files /dev/null and b/assets/images/logo_onap_2017.png differ diff --git a/assets/sdc-icons/README.md b/assets/sdc-icons/README.md new file mode 100644 index 0000000..dc3e798 --- /dev/null +++ b/assets/sdc-icons/README.md @@ -0,0 +1,9 @@ +components folder contain icon that are not part of sdc-icon component. +-Icon naming: +------------------ +- +--- +- +-name = if contains multiple words, use "_" +-type = circle/square/triangle +-o = outline (for all icons that have white background) diff --git a/assets/sdc-icons/alert-triangle-o.svg b/assets/sdc-icons/alert-triangle-o.svg new file mode 100644 index 0000000..c1d8d0a --- /dev/null +++ b/assets/sdc-icons/alert-triangle-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/alert-triangle.svg b/assets/sdc-icons/alert-triangle.svg new file mode 100644 index 0000000..ed3f6f8 --- /dev/null +++ b/assets/sdc-icons/alert-triangle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/sdc-icons/api-o.svg b/assets/sdc-icons/api-o.svg new file mode 100644 index 0000000..169630a --- /dev/null +++ b/assets/sdc-icons/api-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/arrow2-right-child.svg b/assets/sdc-icons/arrow2-right-child.svg new file mode 100644 index 0000000..0123a82 --- /dev/null +++ b/assets/sdc-icons/arrow2-right-child.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/arrow2-right.svg b/assets/sdc-icons/arrow2-right.svg new file mode 100644 index 0000000..b8b808a --- /dev/null +++ b/assets/sdc-icons/arrow2-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/arrow3-down-o.svg b/assets/sdc-icons/arrow3-down-o.svg new file mode 100644 index 0000000..401a7f8 --- /dev/null +++ b/assets/sdc-icons/arrow3-down-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/arrow3-up-o.svg b/assets/sdc-icons/arrow3-up-o.svg new file mode 100644 index 0000000..331634f --- /dev/null +++ b/assets/sdc-icons/arrow3-up-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/attachment.svg b/assets/sdc-icons/attachment.svg new file mode 100644 index 0000000..a1d7768 --- /dev/null +++ b/assets/sdc-icons/attachment.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/bedge.svg b/assets/sdc-icons/bedge.svg new file mode 100644 index 0000000..f43ac9b --- /dev/null +++ b/assets/sdc-icons/bedge.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/sdc-icons/browse.svg b/assets/sdc-icons/browse.svg new file mode 100644 index 0000000..c63620f --- /dev/null +++ b/assets/sdc-icons/browse.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/calendar-o.svg b/assets/sdc-icons/calendar-o.svg new file mode 100644 index 0000000..93f8719 --- /dev/null +++ b/assets/sdc-icons/calendar-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/camera-o.svg b/assets/sdc-icons/camera-o.svg new file mode 100644 index 0000000..86f1f13 --- /dev/null +++ b/assets/sdc-icons/camera-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/caret1-down-o.svg b/assets/sdc-icons/caret1-down-o.svg new file mode 100644 index 0000000..724152a --- /dev/null +++ b/assets/sdc-icons/caret1-down-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/caret2-right-circle-o.svg b/assets/sdc-icons/caret2-right-circle-o.svg new file mode 100644 index 0000000..2715dad --- /dev/null +++ b/assets/sdc-icons/caret2-right-circle-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/caret2-right-circle.svg b/assets/sdc-icons/caret2-right-circle.svg new file mode 100644 index 0000000..e585c1d --- /dev/null +++ b/assets/sdc-icons/caret2-right-circle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/sdc-icons/caret3-right.svg b/assets/sdc-icons/caret3-right.svg new file mode 100644 index 0000000..1eefc47 --- /dev/null +++ b/assets/sdc-icons/caret3-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/close.svg b/assets/sdc-icons/close.svg new file mode 100644 index 0000000..3c884d2 --- /dev/null +++ b/assets/sdc-icons/close.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/commit-o.svg b/assets/sdc-icons/commit-o.svg new file mode 100644 index 0000000..e71c18e --- /dev/null +++ b/assets/sdc-icons/commit-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/components/checkbox_checked.svg b/assets/sdc-icons/components/checkbox_checked.svg new file mode 100644 index 0000000..520c7b6 --- /dev/null +++ b/assets/sdc-icons/components/checkbox_checked.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/components/checkbox_disabled.svg b/assets/sdc-icons/components/checkbox_disabled.svg new file mode 100644 index 0000000..a032573 --- /dev/null +++ b/assets/sdc-icons/components/checkbox_disabled.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/sdc-icons/components/radio_checked .svg b/assets/sdc-icons/components/radio_checked .svg new file mode 100644 index 0000000..534d05d --- /dev/null +++ b/assets/sdc-icons/components/radio_checked .svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/sdc-icons/components/radio_disabled.svg b/assets/sdc-icons/components/radio_disabled.svg new file mode 100644 index 0000000..0906f66 --- /dev/null +++ b/assets/sdc-icons/components/radio_disabled.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/sdc-icons/composition-o.svg b/assets/sdc-icons/composition-o.svg new file mode 100644 index 0000000..5648cda --- /dev/null +++ b/assets/sdc-icons/composition-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/copy-o.svg b/assets/sdc-icons/copy-o.svg new file mode 100644 index 0000000..eeee9aa --- /dev/null +++ b/assets/sdc-icons/copy-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/deployment-artifacts-o.svg b/assets/sdc-icons/deployment-artifacts-o.svg new file mode 100644 index 0000000..e5091cd --- /dev/null +++ b/assets/sdc-icons/deployment-artifacts-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/description-o.svg b/assets/sdc-icons/description-o.svg new file mode 100644 index 0000000..d4e4345 --- /dev/null +++ b/assets/sdc-icons/description-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/distributed.svg b/assets/sdc-icons/distributed.svg new file mode 100644 index 0000000..956c51f --- /dev/null +++ b/assets/sdc-icons/distributed.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/download-o.svg b/assets/sdc-icons/download-o.svg new file mode 100644 index 0000000..f54dba7 --- /dev/null +++ b/assets/sdc-icons/download-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/edit-file-o.svg b/assets/sdc-icons/edit-file-o.svg new file mode 100644 index 0000000..9f17daa --- /dev/null +++ b/assets/sdc-icons/edit-file-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/edit-o.svg b/assets/sdc-icons/edit-o.svg new file mode 100644 index 0000000..bfa434e --- /dev/null +++ b/assets/sdc-icons/edit-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/expand-o.svg b/assets/sdc-icons/expand-o.svg new file mode 100644 index 0000000..c7127d7 --- /dev/null +++ b/assets/sdc-icons/expand-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/eye-o.svg b/assets/sdc-icons/eye-o.svg new file mode 100644 index 0000000..35cf012 --- /dev/null +++ b/assets/sdc-icons/eye-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/filter-o.svg b/assets/sdc-icons/filter-o.svg new file mode 100644 index 0000000..1364bc5 --- /dev/null +++ b/assets/sdc-icons/filter-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/info-circle-o.svg b/assets/sdc-icons/info-circle-o.svg new file mode 100644 index 0000000..4d91259 --- /dev/null +++ b/assets/sdc-icons/info-circle-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/info-circle.svg b/assets/sdc-icons/info-circle.svg new file mode 100644 index 0000000..551966a --- /dev/null +++ b/assets/sdc-icons/info-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/sdc-icons/info-square-o.svg b/assets/sdc-icons/info-square-o.svg new file mode 100644 index 0000000..40a85c6 --- /dev/null +++ b/assets/sdc-icons/info-square-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/inputs-o.svg b/assets/sdc-icons/inputs-o.svg new file mode 100644 index 0000000..5120b24 --- /dev/null +++ b/assets/sdc-icons/inputs-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/locked.svg b/assets/sdc-icons/locked.svg new file mode 100644 index 0000000..5c0939f --- /dev/null +++ b/assets/sdc-icons/locked.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/minus-circle.svg b/assets/sdc-icons/minus-circle.svg new file mode 100644 index 0000000..d7755dc --- /dev/null +++ b/assets/sdc-icons/minus-circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/minus.svg b/assets/sdc-icons/minus.svg new file mode 100644 index 0000000..48fdcae --- /dev/null +++ b/assets/sdc-icons/minus.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/notifications-o.svg b/assets/sdc-icons/notifications-o.svg new file mode 100644 index 0000000..f52a4e3 --- /dev/null +++ b/assets/sdc-icons/notifications-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/plus-circle-o.svg b/assets/sdc-icons/plus-circle-o.svg new file mode 100644 index 0000000..c87e574 --- /dev/null +++ b/assets/sdc-icons/plus-circle-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/plus-circle.svg b/assets/sdc-icons/plus-circle.svg new file mode 100644 index 0000000..62840b5 --- /dev/null +++ b/assets/sdc-icons/plus-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/sdc-icons/plus.svg b/assets/sdc-icons/plus.svg new file mode 100644 index 0000000..00a6673 --- /dev/null +++ b/assets/sdc-icons/plus.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/profile-o.svg b/assets/sdc-icons/profile-o.svg new file mode 100644 index 0000000..802715e --- /dev/null +++ b/assets/sdc-icons/profile-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/profiles-o.svg b/assets/sdc-icons/profiles-o.svg new file mode 100644 index 0000000..ac9c673 --- /dev/null +++ b/assets/sdc-icons/profiles-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/question-mark-circle-o.svg b/assets/sdc-icons/question-mark-circle-o.svg new file mode 100644 index 0000000..59dabde --- /dev/null +++ b/assets/sdc-icons/question-mark-circle-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/question-mark-circle.svg b/assets/sdc-icons/question-mark-circle.svg new file mode 100644 index 0000000..ae5541a --- /dev/null +++ b/assets/sdc-icons/question-mark-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/sdc-icons/req-capabilities-o.svg b/assets/sdc-icons/req-capabilities-o.svg new file mode 100644 index 0000000..88ace7d --- /dev/null +++ b/assets/sdc-icons/req-capabilities-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/revert-o.svg b/assets/sdc-icons/revert-o.svg new file mode 100644 index 0000000..d48b8d0 --- /dev/null +++ b/assets/sdc-icons/revert-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/save-o.svg b/assets/sdc-icons/save-o.svg new file mode 100644 index 0000000..3b95518 --- /dev/null +++ b/assets/sdc-icons/save-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/search-o.svg b/assets/sdc-icons/search-o.svg new file mode 100644 index 0000000..312e21a --- /dev/null +++ b/assets/sdc-icons/search-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/settings-o.svg b/assets/sdc-icons/settings-o.svg new file mode 100644 index 0000000..888be2e --- /dev/null +++ b/assets/sdc-icons/settings-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/spinner.svg b/assets/sdc-icons/spinner.svg new file mode 100644 index 0000000..9d0efb8 --- /dev/null +++ b/assets/sdc-icons/spinner.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/sdc-icons/success-circle-o.svg b/assets/sdc-icons/success-circle-o.svg new file mode 100644 index 0000000..2d58e6c --- /dev/null +++ b/assets/sdc-icons/success-circle-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/success.svg b/assets/sdc-icons/success.svg new file mode 100644 index 0000000..918833b --- /dev/null +++ b/assets/sdc-icons/success.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/sync-o.svg b/assets/sdc-icons/sync-o.svg new file mode 100644 index 0000000..88a64d1 --- /dev/null +++ b/assets/sdc-icons/sync-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/trash-o.svg b/assets/sdc-icons/trash-o.svg new file mode 100644 index 0000000..7a841fb --- /dev/null +++ b/assets/sdc-icons/trash-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/undo-o.svg b/assets/sdc-icons/undo-o.svg new file mode 100644 index 0000000..f6f0dd1 --- /dev/null +++ b/assets/sdc-icons/undo-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/unlocked-o.svg b/assets/sdc-icons/unlocked-o.svg new file mode 100644 index 0000000..6e677e0 --- /dev/null +++ b/assets/sdc-icons/unlocked-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/upload-o.svg b/assets/sdc-icons/upload-o.svg new file mode 100644 index 0000000..bf62707 --- /dev/null +++ b/assets/sdc-icons/upload-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/v-circle-o.svg b/assets/sdc-icons/v-circle-o.svg new file mode 100644 index 0000000..1d6f737 --- /dev/null +++ b/assets/sdc-icons/v-circle-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/v-circle.svg b/assets/sdc-icons/v-circle.svg new file mode 100644 index 0000000..9baa0f2 --- /dev/null +++ b/assets/sdc-icons/v-circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/x-circle-o.svg b/assets/sdc-icons/x-circle-o.svg new file mode 100644 index 0000000..ec0d80b --- /dev/null +++ b/assets/sdc-icons/x-circle-o.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/sdc-icons/x-circle.svg b/assets/sdc-icons/x-circle.svg new file mode 100644 index 0000000..15982ce --- /dev/null +++ b/assets/sdc-icons/x-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/components/accordion/accordion-basic.html b/components/accordion/accordion-basic.html new file mode 100644 index 0000000..fe85473 --- /dev/null +++ b/components/accordion/accordion-basic.html @@ -0,0 +1,22 @@ +
+
+
+ + + + + + +
+
+ Accordion Title +
+
+
+
+ Accordion body +
+
+
\ No newline at end of file diff --git a/components/accordion/accordion.scss b/components/accordion/accordion.scss new file mode 100644 index 0000000..ef65b9c --- /dev/null +++ b/components/accordion/accordion.scss @@ -0,0 +1,50 @@ +.sdc-accordion { + display: flex; + flex-direction: column; + margin-bottom: 10px; + &.disabled { + opacity: .4; + pointer-events: none; + } + &:not(.disabled) { + .sdc-accordion-header { + cursor: pointer; + } + } + .sdc-accordion-header { + display: flex; + flex-direction: row; + .svg-icon-wrapper { + margin-right: 20px; + transition: transform 0.4s; + &.down { + transform: rotate(180deg); + } + .svg-icon { + fill: $gray; + width: 14px; + height: 8px; + } + } + &.arrow-right { + justify-content: space-between; + .svg-icon-wrapper{ + order:1; + margin:0; + } + } + } + .sdc-accordion-body { + padding-left: 10px; + opacity: 0; + overflow-y: hidden; + max-height: 0; + padding-top: 0px; + transition: opacity 0.33s linear, padding-top 0.3s linear; + &.open { + padding-top: 5px; + opacity: 1; + max-height: 9999px; + } + } +} diff --git a/components/autocomplete/_autocomplete.scss b/components/autocomplete/_autocomplete.scss new file mode 100644 index 0000000..7275156 --- /dev/null +++ b/components/autocomplete/_autocomplete.scss @@ -0,0 +1,43 @@ +.sdc-autocomplete-container{ + position: relative; + + &.results-shown{ + .sdc-input__input{ + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + } + } + + ul.autocomplete-results { + opacity: 0; + border: solid 1px $blue; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-top:none; + background-color: $white; + max-height: 200px; + box-shadow: 0 3px 7px -3px $dark-gray; + overflow-y: scroll; + overflow-x: hidden; + position: absolute; + box-sizing: border-box; + width: 100%; + left: 0; + top: 100%; + + li { + color:$text-black; + text-indent: 10px; + line-height: 30px; + cursor:pointer; + @include body-1; + + &:hover { + background-color: $lighter-blue; + color: $blue; + } + } + + } + + } diff --git a/components/autocomplete/autocomlete-close.html b/components/autocomplete/autocomlete-close.html new file mode 100644 index 0000000..93946bb --- /dev/null +++ b/components/autocomplete/autocomlete-close.html @@ -0,0 +1,22 @@ +
+
+
+ + +
+ + + + + + + + + + +
+
+ +
+
+ diff --git a/components/autocomplete/autocomplete-open.html b/components/autocomplete/autocomplete-open.html new file mode 100644 index 0000000..01b1f40 --- /dev/null +++ b/components/autocomplete/autocomplete-open.html @@ -0,0 +1,24 @@ +
+
+
+ + +
+ + + + + + + + + + +
+
+
red
+
yellow
+
orange
+
green
+
+
diff --git a/components/button/_button.scss b/components/button/_button.scss new file mode 100644 index 0000000..3c21a45 --- /dev/null +++ b/components/button/_button.scss @@ -0,0 +1,168 @@ +.sdc-button { + order:1; + @include box-sizing; + display: inline-flex; + align-items: center; + justify-content: center; + flex-direction: row; + outline: none; + border-radius: 2px; + padding: 0 12px; + height: 36px; + line-height: 36px; + width: 120px; + min-width: 90px; + cursor: pointer; + text-align: center; + text-transform: uppercase; + @include body-1; + + &:disabled { + cursor: default; + } + + // Primary button + &.sdc-button__primary { + border: 1px solid transparent; + background-color: $blue; + color: $white; + + &:not(:disabled) { + &:hover, &:active { + background-color: $light-blue; + } + &:focus:not(:active) { + border: 1px solid $white; + background-color: $light-blue; + box-shadow: 0px 0px 0px 1px $light-blue; + } + } + + &:disabled{ + background: $blue-disabled; + } + } + + // Secondary button + &.sdc-button__secondary { + border: 1px solid $blue; + background-color: transparent; + color: $blue; + + &:not(:disabled) { + &:hover, &:active { + background-color: $light-blue; + color:$white; + } + &:focus:not(:active) { + color: $light-blue; + box-shadow: inset 0px 0px 0px 0px $dark-blue, 0px 0px 0px 1px $blue; + &:hover { + color: $white; + } + } + } + + &:disabled { + color: $blue-disabled; + border-color: $blue-disabled; + } + } + + // Link button + &.sdc-button__link { + background-color: transparent; + color: $blue; + fill: $blue; + border: none; + + &:not(:disabled) { + &:hover, &:active { + color: $light-blue; + } + &:focus:not(:active) { + border: 1px solid $dark-blue; + color: $light-blue; + } + } + + &:disabled{ + color: $blue-disabled; + } + } + + + // alert button + &.sdc-button__alert { + border: none; + background-color: $red; + color: $white; + + &:not(:disabled) { + &:hover, &:active { + background-color: $light-red; + } + &:focus:not(:active) { + border: 0.5px solid $white; + background-color: $light-red; + box-shadow: 0px 0px 0px 1px $light-red; + } + } + + &:disabled{ + background: $disabled-red; + } + } + + + /*** Sizes ***/ + &.btn-large{ + width: $btn-large; + } + + &.btn-medium{ + width: $btn-medium; + } + + &.btn-small{ + width: $btn-small; + } + + &.btn-x-small{ + width: $btn-extra-small; + } + + &.btn-default{ + width: $btn-default; + } + + /*** Buttons with icons ***/ + &.sdc-icon-right { + flex-direction: row-reverse; + .svg-icon { + margin-left: 15px; + } + } + + &.sdc-icon-left { + flex-direction: row; + .svg-icon { + margin-right: 15px; + } + } + + svg { + display: inline-block; + vertical-align: middle; + } +} +.sdc-button__wrapper { + display: inline-flex; +} +.sdc-button__spinner { + padding-top: 6px; + margin:0 2px; + &.left { + order:2; + } +} diff --git a/components/button/button-link-auto.html b/components/button/button-link-auto.html new file mode 100644 index 0000000..22ac4c8 --- /dev/null +++ b/components/button/button-link-auto.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-link-disabled.html b/components/button/button-link-disabled.html new file mode 100644 index 0000000..9267620 --- /dev/null +++ b/components/button/button-link-disabled.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-link-extra-small.html b/components/button/button-link-extra-small.html new file mode 100644 index 0000000..245f885 --- /dev/null +++ b/components/button/button-link-extra-small.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-link-large.html b/components/button/button-link-large.html new file mode 100644 index 0000000..6d1780c --- /dev/null +++ b/components/button/button-link-large.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-link-medium.html b/components/button/button-link-medium.html new file mode 100644 index 0000000..cb0293d --- /dev/null +++ b/components/button/button-link-medium.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-link-small.html b/components/button/button-link-small.html new file mode 100644 index 0000000..5c195fa --- /dev/null +++ b/components/button/button-link-small.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-link.html b/components/button/button-link.html new file mode 100644 index 0000000..5c2070b --- /dev/null +++ b/components/button/button-link.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-primary-auto.html b/components/button/button-primary-auto.html new file mode 100644 index 0000000..125276f --- /dev/null +++ b/components/button/button-primary-auto.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-primary-disabled.html b/components/button/button-primary-disabled.html new file mode 100644 index 0000000..b2ef842 --- /dev/null +++ b/components/button/button-primary-disabled.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-primary-extra-small.html b/components/button/button-primary-extra-small.html new file mode 100644 index 0000000..a3be965 --- /dev/null +++ b/components/button/button-primary-extra-small.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-primary-large.html b/components/button/button-primary-large.html new file mode 100644 index 0000000..c0a41b1 --- /dev/null +++ b/components/button/button-primary-large.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-primary-medium.html b/components/button/button-primary-medium.html new file mode 100644 index 0000000..9ddedc5 --- /dev/null +++ b/components/button/button-primary-medium.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-primary-small.html b/components/button/button-primary-small.html new file mode 100644 index 0000000..847f753 --- /dev/null +++ b/components/button/button-primary-small.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-primary.html b/components/button/button-primary.html new file mode 100644 index 0000000..b1524bf --- /dev/null +++ b/components/button/button-primary.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-secondary-auto.html b/components/button/button-secondary-auto.html new file mode 100644 index 0000000..a183ad8 --- /dev/null +++ b/components/button/button-secondary-auto.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-secondary-disabled.html b/components/button/button-secondary-disabled.html new file mode 100644 index 0000000..4125328 --- /dev/null +++ b/components/button/button-secondary-disabled.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-secondary-extra-small.html b/components/button/button-secondary-extra-small.html new file mode 100644 index 0000000..92c4784 --- /dev/null +++ b/components/button/button-secondary-extra-small.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-secondary-large.html b/components/button/button-secondary-large.html new file mode 100644 index 0000000..958c151 --- /dev/null +++ b/components/button/button-secondary-large.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-secondary-medium.html b/components/button/button-secondary-medium.html new file mode 100644 index 0000000..67f9741 --- /dev/null +++ b/components/button/button-secondary-medium.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-secondary-small.html b/components/button/button-secondary-small.html new file mode 100644 index 0000000..d9d8cd7 --- /dev/null +++ b/components/button/button-secondary-small.html @@ -0,0 +1,3 @@ + diff --git a/components/button/button-secondary.html b/components/button/button-secondary.html new file mode 100644 index 0000000..64967cc --- /dev/null +++ b/components/button/button-secondary.html @@ -0,0 +1,3 @@ + diff --git a/components/checkbox/_checkbox.scss b/components/checkbox/_checkbox.scss new file mode 100644 index 0000000..c35c8e0 --- /dev/null +++ b/components/checkbox/_checkbox.scss @@ -0,0 +1,66 @@ +.sdc-checkbox { + line-height: 14px; + + label { + position: relative; + display: block; + padding-left: 14px; + } + + .sdc-checkbox__input { + appearance: none; + -moz-appearance: none; + -webkit-appearance: none; + position: absolute; + z-index: -1; + opacity: 0; + + // Checkbox not checked + + .sdc-checkbox__label:before { + display: inline-block; + position: absolute; + left: 0; + top: 0; + content: ""; + width: 14px; + height: 14px; + box-sizing: content-box; + background: no-repeat url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%3E%20%3Cdefs%3E%20%3Cpath%20id%3D%22disabled-a%22%20d%3D%22M2%2C0%20L12%2C-2.22044605e-16%20C13.1045695%2C5.56104062e-16%2014%2C0.8954305%2014%2C2%20L14%2C12%20C14%2C13.1045695%2013.1045695%2C14%2012%2C14%20L2%2C14%20C0.8954305%2C14%208.94280938e-16%2C13.1045695%20-2.22044605e-16%2C12%20L-2.22044605e-16%2C2%20C-3.57315355e-16%2C0.8954305%200.8954305%2C-1.91384796e-17%202%2C-2.22044605e-16%20Z%22%2F%3E%20%3C%2Fdefs%3E%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%20%3Cuse%20fill%3D%22%23FFFFFF%22%20xlink%3Ahref%3D%22%23disabled-a%22%2F%3E%20%3Cpath%20stroke%3D%22%23D2D2D2%22%20d%3D%22M2%2C0.5%20C1.17157288%2C0.5%200.5%2C1.17157288%200.5%2C2%20L0.5%2C12%20C0.5%2C12.8284271%201.17157288%2C13.5%202%2C13.5%20L12%2C13.5%20C12.8284271%2C13.5%2013.5%2C12.8284271%2013.5%2C12%20L13.5%2C2%20C13.5%2C1.17157288%2012.8284271%2C0.5%2012%2C0.5%20L2%2C0.5%20Z%22%2F%3E%20%3C%2Fg%3E%20%3C%2Fsvg%3E'); + } + + // Checkbox disabled and not checked + &:disabled { + + .sdc-checkbox__label { + color: $gray; + &:before { + background: no-repeat url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%3E%20%3Cdefs%3E%20%3Cpath%20id%3D%22disabled-a%22%20d%3D%22M2%2C0%20L12%2C-2.22044605e-16%20C13.1045695%2C5.56104062e-16%2014%2C0.8954305%2014%2C2%20L14%2C12%20C14%2C13.1045695%2013.1045695%2C14%2012%2C14%20L2%2C14%20C0.8954305%2C14%208.94280938e-16%2C13.1045695%20-2.22044605e-16%2C12%20L-2.22044605e-16%2C2%20C-3.57315355e-16%2C0.8954305%200.8954305%2C-1.91384796e-17%202%2C-2.22044605e-16%20Z%22%2F%3E%20%3C%2Fdefs%3E%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%20%3Cuse%20fill%3D%22%23F2F2F2%22%20xlink%3Ahref%3D%22%23disabled-a%22%2F%3E%20%3Cpath%20stroke%3D%22%23D2D2D2%22%20d%3D%22M2%2C0.5%20C1.17157288%2C0.5%200.5%2C1.17157288%200.5%2C2%20L0.5%2C12%20C0.5%2C12.8284271%201.17157288%2C13.5%202%2C13.5%20L12%2C13.5%20C12.8284271%2C13.5%2013.5%2C12.8284271%2013.5%2C12%20L13.5%2C2%20C13.5%2C1.17157288%2012.8284271%2C0.5%2012%2C0.5%20L2%2C0.5%20Z%22%2F%3E%20%3C%2Fg%3E%20%3C%2Fsvg%3E'); + } + } + } + + &:checked { + // Checkbox checked + + .sdc-checkbox__label:before { + background: no-repeat url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%20fill%3D%22%23009fdb%22%3E%3Cpath%20d%3D%22M2%2C0%20L12%2C-2.22044605e-16%20C13.1045695%2C5.56104062e-16%2014%2C0.8954305%2014%2C2%20L14%2C12%20C14%2C13.1045695%2013.1045695%2C14%2012%2C14%20L2%2C14%20C0.8954305%2C14%208.94280938e-16%2C13.1045695%20-2.22044605e-16%2C12%20L-2.22044605e-16%2C2%20C-3.57315355e-16%2C0.8954305%200.8954305%2C-1.91384796e-17%202%2C-2.22044605e-16%20Z%20M3.85355339%2C7.54977605%20C3.65829124%2C7.35451391%203.34170876%2C7.35451391%203.14644661%2C7.54977605%20C2.95118446%2C7.7450382%202.95118446%2C8.06162069%203.14644661%2C8.25688283%20L5.71469032%2C10.8251265%20C5.93114093%2C11.0415771%206.28952386%2C11.0144698%206.47095446%2C10.7679244%20L10.8653572%2C4.79638422%20C11.0290275%2C4.57397322%2010.9814087%2C4.26099251%2010.7589977%2C4.09732224%20C10.5365867%2C3.93365198%2010.223606%2C3.98127076%2010.0599357%2C4.20368177%20L6.01038326%2C9.70660592%20L3.85355339%2C7.54977605%20Z%22%2F%3E%3C%2Fsvg%3E'); + } + + // Checkbox disabled and checked + &:disabled { + + .sdc-checkbox__label:before { + background: no-repeat url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%3E%3Cpath%20fill%3D%22%23d2d2d2%22%20fill-rule%3D%22evenodd%22%20d%3D%22M2%2C0%20L12%2C-2.22044605e-16%20C13.1045695%2C5.56104062e-16%2014%2C0.8954305%2014%2C2%20L14%2C12%20C14%2C13.1045695%2013.1045695%2C14%2012%2C14%20L2%2C14%20C0.8954305%2C14%208.94280938e-16%2C13.1045695%20-2.22044605e-16%2C12%20L-2.22044605e-16%2C2%20C-3.57315355e-16%2C0.8954305%200.8954305%2C-1.91384796e-17%202%2C-2.22044605e-16%20Z%20M3.85355339%2C7.54977605%20C3.65829124%2C7.35451391%203.34170876%2C7.35451391%203.14644661%2C7.54977605%20C2.95118446%2C7.7450382%202.95118446%2C8.06162069%203.14644661%2C8.25688283%20L5.71469032%2C10.8251265%20C5.93114093%2C11.0415771%206.28952386%2C11.0144698%206.47095446%2C10.7679244%20L10.8653572%2C4.79638422%20C11.0290275%2C4.57397322%2010.9814087%2C4.26099251%2010.7589977%2C4.09732224%20C10.5365867%2C3.93365198%2010.223606%2C3.98127076%2010.0599357%2C4.20368177%20L6.01038326%2C9.70660592%20L3.85355339%2C7.54977605%20Z%22%2F%3E%3C%2Fsvg%3E'); + } + } + } + + &:not(:disabled) { + + .sdc-checkbox__label { + cursor: pointer; + } + } + } + + .sdc-checkbox__label:not(:empty) { + padding-left: 14px; + @include body-1; + } +} diff --git a/components/checkbox/checkbox-checked.html b/components/checkbox/checkbox-checked.html new file mode 100644 index 0000000..de0c0d8 --- /dev/null +++ b/components/checkbox/checkbox-checked.html @@ -0,0 +1,6 @@ +
+ +
diff --git a/components/checkbox/checkbox-disabled-checked.html b/components/checkbox/checkbox-disabled-checked.html new file mode 100644 index 0000000..53e77ec --- /dev/null +++ b/components/checkbox/checkbox-disabled-checked.html @@ -0,0 +1,6 @@ +
+ +
diff --git a/components/checkbox/checkbox-disabled.html b/components/checkbox/checkbox-disabled.html new file mode 100644 index 0000000..2425218 --- /dev/null +++ b/components/checkbox/checkbox-disabled.html @@ -0,0 +1,6 @@ +
+ +
diff --git a/components/checkbox/checkbox-unchecked.html b/components/checkbox/checkbox-unchecked.html new file mode 100644 index 0000000..9cc3d1c --- /dev/null +++ b/components/checkbox/checkbox-unchecked.html @@ -0,0 +1,6 @@ +
+ +
diff --git a/components/checklist/_checklist.scss b/components/checklist/_checklist.scss new file mode 100644 index 0000000..248993d --- /dev/null +++ b/components/checklist/_checklist.scss @@ -0,0 +1,21 @@ +$space-lines: 14px; +$padding-for-sub-level: 28px; +.checkbox-item{ + margin: $space-lines 0; + .sdc-checkbox__label{ + @include body-2-emphasis; + } + .semi-checked{ + .sdc-checkbox__label:before{ + background: no-repeat url('data:image/svg+xml;utf8,'); + } + } +} +.checkbox-sublist{ + padding-left: $padding-for-sub-level; + .checkbox-item{ + .sdc-checkbox__label{ + @include body-2; + } + } +} diff --git a/components/checklist/checklist-with-checked-items.html b/components/checklist/checklist-with-checked-items.html new file mode 100644 index 0000000..e1adbd7 --- /dev/null +++ b/components/checklist/checklist-with-checked-items.html @@ -0,0 +1,24 @@ +
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
diff --git a/components/checklist/checklist-with-disabled-items.html b/components/checklist/checklist-with-disabled-items.html new file mode 100644 index 0000000..f001ec1 --- /dev/null +++ b/components/checklist/checklist-with-disabled-items.html @@ -0,0 +1,25 @@ +
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+ diff --git a/components/checklist/multi-levels-checklist.html b/components/checklist/multi-levels-checklist.html new file mode 100644 index 0000000..9aa136e --- /dev/null +++ b/components/checklist/multi-levels-checklist.html @@ -0,0 +1,50 @@ +
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
diff --git a/components/checklist/simple-checklist.html b/components/checklist/simple-checklist.html new file mode 100644 index 0000000..1fe55da --- /dev/null +++ b/components/checklist/simple-checklist.html @@ -0,0 +1,24 @@ +
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
diff --git a/components/dropdown/_dropdown.scss b/components/dropdown/_dropdown.scss new file mode 100644 index 0000000..908bbaa --- /dev/null +++ b/components/dropdown/_dropdown.scss @@ -0,0 +1,346 @@ +.sdc-dropdown { + @include body-1; + position: relative; + display: block; + + .sdc-dropdown__error--block { + display: none; + } + + &.headless { + display: none; + } + + /************************************* + SDC DropDown styles + *************************************/ + .sdc-dropdown__component-container { + position: relative; + height: 40px; + + .sdc-dropdown__header { + background: $white; + text-align: left; + position: relative; + color: $dark-gray; + @include base-font-regular; + font-size: 14px; + text-indent: 6px; + border: solid 1px $light-gray; + width: 100%; + height: 40px; + line-height: 36px; + box-sizing: border-box; + border-radius: 2px; + + &.placeholder { + @include base-font-italic; + color: $gray; + } + + &.disabled { + border: solid 1px $light-gray; + background-color: $light-silver; + color: $light-gray; + cursor: default; + + &:focus { + border: solid 1px $light-gray; + outline: none; + .svg-icon>svg { + fill: $light-gray; + } + } + .svg-icon>svg { + fill: $light-gray; + } + } + + &:focus { + border: solid 1px $light-blue; + outline: none; + .svg-icon>svg { + fill: $light-blue; + } + } + + .sdc-dropdown-handle { + float: right; + .svg-icon>svg { + fill: $dark-gray; + } + } + + svg-icon { + margin: 10px 6px; + float: right; + } + + } + + } + + &.open-bottom { + .sdc-dropdown__header { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + + border: 1px solid $light-blue; + box-sizing: border-box; + .svg-icon>svg { + fill: $light-blue; + } + + } + } + + &.open-top { + .sdc-dropdown__header { + border-top-left-radius: 0; + border-top-right-radius: 0; + } + } + + .sdc-dropdown__options-list { + position: relative; + opacity: 0; + top: 100%; + left: 0; + width: 100%; + max-height: 0; + overflow-y: auto; + box-sizing: border-box; + border: 1px solid $light-blue; + background-color: white; + box-shadow: 0 3px 7px -3px $dark-gray; + z-index: 999; + &.sdc-dropdown__options-wrapper--top { + bottom: 40px; + border-top: 1px solid $light-blue; + } + &.sdc-dropdown__options-list--headless { + border-top: 1px solid $light-blue; + } + .sdc-dropdown__option { + @include base-font-regular; + font-size: 14px; + text-indent: 10px; + padding: 10px; + background: transparent; + cursor: pointer; + height: 40px; + box-sizing: border-box; + &.sdc-dropdown__option--hr { + height: 1px; + overflow: hidden; + border-top: 1px solid $silver; + padding: 0; + margin: 10px 20px; + } + &.sdc-dropdown__option--group { + text-indent: 30px; + } + &:hover { + background-color: $light-silver; + //@include base-font-semibold; + } + &.selected { + background-color: $lighter-blue; + color: $blue; + @include base-font-semibold; + } + &.sdc-dropdown__option--header { + @include base-font-semibold; + color: $text-black; + cursor: default; + &.sdc-dropdown__option--group { + text-indent: 10px; + } + &:hover { + background-color: transparent; + } + } + &.sdc-dropdown__option--disabled { + color: $gray; + cursor: default; + &:hover { + background-color: transparent; + } + &::after { + color: $gray; + } + &:focus { + border: solid 1px $light-gray; + outline: none; + } + } + } + } + .sdc-dropdown__select { + @include base-font-regular; + text-indent: 6px; + border: solid 1px $light-gray; + width: 100%; + &.disabled { + opacity: 0.7; + } + option { + padding: 3px; + } + } + .sdc-dropdown__label { + margin-bottom: 5px; + display: block; + @include body-3-emphasis; + color: $text-black; + &.required::before { + content: '*'; + color: $red; + margin: 0 4px 0 0; + } + } + + /************************************* + SDC Auto-DropDown styles + *************************************/ + &.open-bottom { + .sdc-dropdown-auto__wrapper { + border: 1px solid $light-blue; + box-sizing: border-box; + .svg-icon>svg { + fill: $light-blue; + } + } + } + + .sdc-dropdown-auto__wrapper { + display: flex; + border: solid 1px $light-gray; + border-radius: 2px; + + .sdc-dropdown__header { + border: none; + color: $gray; + + &:focus { + border: none; + } + } + + svg-icon { + margin: 12px 10px; + float: right; + } + } + + /************************************* + SDC Error styles + *************************************/ + &.sdc-dropdown__error { + .sdc-dropdown__header { + border: solid 1px $red; + @include font-error; + &::after { + @include font-error; + } + &:focus { + border: solid 1px $red; + .sdc-dropdown-handle { + use { + fill: $red; + } + } + } + .sdc-dropdown-handle { + use { + fill: $red; + } + } + } + .sdc-dropdown__error--block { + display: block; + @include font-error; + margin: 4px 0; + @include body-3; + } + .sdc-dropdown__options-wrapper--frame { + .sdc-dropdown__options-list { + border: 1px solid $red; + border-top: none; + } + &.sdc-dropdown__options-wrapper--top { + .sdc-dropdown__options-list { + border-top: 1px solid $red; + } + } + } + .svg-icon.__exclamationTriangleFull { + width: 12px; + height: 10px; + margin-right: 6px; + } + } +} + +.sdc-dropdown__error--icon { + fill: $red; +} + +/********************************************************/ + +/* Animation */ + +/********************************************************/ + +@include keyframes-expand-animation('top-to-bottom-exp', 244px); +@include keyframes-collapse-animation('top-to-bottom', 244px); +@include keyframes-expand-animation('bottom-to-top-exp', 244px); +@include keyframes-collapse-animation('bottom-to-top', 244px); +.sdc-dropdown__options-wrapper--frame { + overflow: hidden; + position: absolute; + top: auto; + width: 100%; + &.sdc-dropdown__options-wrapper--top { + bottom: 40px; + top: auto; + padding: 10px 0 0 0; + /* Expend animation from bottom to top */ + .sdc-dropdown__options-list { + border-top: 1px solid $light-blue; + box-shadow: 0 0 7px -1px $dark-gray; + &.sdc-dropdown__options-list--animation-init { + border-bottom: none; + padding: 0; + animation: bottom-to-top .0s forwards; + } + } + &.sdc-dropdown__options-wrapper--uncollapsed { + .sdc-dropdown__options-list { + animation: bottom-to-top-exp .0s forwards; + } + } + } +} + +/** +Fold animation from top to bottom + */ + +.sdc-dropdown { + .sdc-dropdown__options-list.sdc-dropdown__options-list--animation-init { + animation: top-to-bottom .0s forwards; + } +} + +/** +Expend animation from top to bottom + */ + +.sdc-dropdown { + .sdc-dropdown__options-wrapper--uncollapsed { + .sdc-dropdown__options-list.sdc-dropdown__options-list--animation-init { + animation: top-to-bottom-exp .0s forwards; + } + } +} diff --git a/components/dropdown/dropdown-disabled.html b/components/dropdown/dropdown-disabled.html new file mode 100644 index 0000000..9e0bd86 --- /dev/null +++ b/components/dropdown/dropdown-disabled.html @@ -0,0 +1,11 @@ +
+ + + +
    +
  • First Option
  • +
  • Second Option
  • +
  • Third Option
  • +
+ +
diff --git a/components/dropdown/dropdown-groups.html b/components/dropdown/dropdown-groups.html new file mode 100644 index 0000000..3e32f16 --- /dev/null +++ b/components/dropdown/dropdown-groups.html @@ -0,0 +1,10 @@ +
+ + +
    +
  • Group 1 title
  • +
  • First Option
  • +
  • Second Option
  • +
  • Third Option
  • +
+
diff --git a/components/dropdown/dropdown-requiered.html b/components/dropdown/dropdown-requiered.html new file mode 100644 index 0000000..4fd555d --- /dev/null +++ b/components/dropdown/dropdown-requiered.html @@ -0,0 +1,18 @@ +
+ + +
    +
  • First Option
  • +
  • Second Option
  • +
  • Third Option
  • +
+
+ + + Error message! +
+
+ diff --git a/components/dropdown/dropdown.html b/components/dropdown/dropdown.html new file mode 100644 index 0000000..440f70b --- /dev/null +++ b/components/dropdown/dropdown.html @@ -0,0 +1,9 @@ +
+ + +
    +
  • First Option
  • +
  • Second Option
  • +
  • Third Option
  • +
+
diff --git a/components/filter-bar/_filter-bar.scss b/components/filter-bar/_filter-bar.scss new file mode 100644 index 0000000..f48a3df --- /dev/null +++ b/components/filter-bar/_filter-bar.scss @@ -0,0 +1,51 @@ +$inputHeight: 38px; + +.sdc-filter-bar { + .sdc-input { + margin: 0; + .sdc-input__input{ + padding-right: 40px; + } + } + + .filter-button{ + position: relative; + float: right; + bottom: $inputHeight; + height: $inputHeight; + right: 11px; + svg{ + position: relative; + top: 50%; + transform: translateY(-50%); + } + } + + .magnify-button { + svg{ + height: 20px; + width: 20px; + path{ + fill: $dark-gray; + } + } + } + + .clear-search-x { + cursor: pointer; + svg{ + height: 14px; + width: 14px; + path{ + fill: $dark-gray; + } + &:hover{ + path{ + fill: $blue; + } + } + } + } +} + + diff --git a/components/filter-bar/filter-bar-with-text.html b/components/filter-bar/filter-bar-with-text.html new file mode 100644 index 0000000..de7a225 --- /dev/null +++ b/components/filter-bar/filter-bar-with-text.html @@ -0,0 +1,16 @@ +
+
+ + + + + + + + + + + + +
+
diff --git a/components/filter-bar/filter-bar.html b/components/filter-bar/filter-bar.html new file mode 100644 index 0000000..90f580d --- /dev/null +++ b/components/filter-bar/filter-bar.html @@ -0,0 +1,17 @@ +
+
+ + + + + + + + + + + + +
+
+ diff --git a/components/icon/_icon.scss b/components/icon/_icon.scss new file mode 100644 index 0000000..cd94eb7 --- /dev/null +++ b/components/icon/_icon.scss @@ -0,0 +1,250 @@ +@mixin color-icon($primary-color, $secondary-color) { + color: $primary-color; + fill: $primary-color; + span{ + color: $primary-color; + } + &:not([disabled]):hover, &:active, &:focus { + &.clickable { + color: $secondary-color; + fill: $secondary-color; + } + } +} + +.svg-icon-wrapper { + display: inline-flex; + justify-content: center; + align-items: center; + + &[disabled] { + opacity: 0.7; + } + + &.bottom { + flex-direction: column; + .svg-icon-label { + margin-bottom: 5px; + } + } + + &.right { + float: none; + .svg-icon-label { + margin-left: 5px; + } + } + + &.top { + flex-direction: column-reverse; + .svg-icon-label { + margin-top: 5px; + } + } + + &.left { + flex-direction: row-reverse; + .svg-icon-label { + margin-right: 5px; + } + } + + &.__warning { + @include color-icon($yellow, $yellow); + } + + &.__primary { + @include color-icon($blue, $light-blue); + } + + &.__secondary { + @include color-icon($gray, $dark-gray); + } + + &.__positive { + @include color-icon($green, $green); + } + + &.__negative { + @include color-icon($red, $red); + } +} + + +.svg-icon { + width: 20px; + height: 20px; + + &.__angleDoubleLeft { + width: 14px; + height: 14px; + } + &.__angleDoubleRight { + width: 14px; + height: 14px; + } + &.__angleLeft { + width: 14px; + height: 14px; + } + &.__angleRight { + width: 14px; + height: 14px; + } + &.__artifacts { + width: 16px; + height: 20px; + } + &.__back { + width: 25px; + height: 25px; + } + &.__base { + // + } + &.__calendar { + // + } + &.__caretDown { + } + &.__check { + } + &.__checkCircle { + width: 16px; + height: 16px; + } + &.__chevronDown{ + width: 10px; + height: 7px; + } + &.__chevronUp { + width: 11px; + height: 7px; + } + &.__close { + width: 10px; + height: 10px; + } + &.__download { + width: 15px; + height: 11px; + } + &.__env { + width: 15px; + height: 14px; + } + &.__error { + width: 14px; + height: 14px; + } + &.__errorCircle { + width: 16px; + height: 16px; + } + &.__exclamationTriangleFull { + width: 15px; + height: 13px; + } + &.__exclamationTriangleLine { + width: 15px; + height: 13px; + } + &.__filter { + // + } + &.__locked { + width: 11px; + } + &.__module { + // + } + &.__nestedHeat { + width: 15px; + height: 13px; + } + &.__network { + width: 13px; + height: 13px; + } + &.__others { + width: 12px; + height: 12px; + } + &.__pencil { + width: 15px; + height: 15px; + } + &.__plus { + width: 9px; + height: 9px; + } + &.__plusCircle { + width: 19px; + height: 19px; + } + &.__plusThin { + width: 9px; + height: 9px; + } + &.__proceedToOverview { + width: 24px; + height: 20px; + } + &.__search { + // + } + &.__sliders { + } + &.__trashO { + width: 15px; + height: 16px; + } + &.__unlocked { + width: 11px; + } + &.__upload { + width: 15px; + height: 11px; + } + &.__vendor { + width: 53px; + height: 47px; + } + &.__versionControllerLockClosed { + width: 21px; + height: 23px; + } + &.__versionControllerLockOpen { + width: 24px; + height: 28px; + } + &.__versionControllerRevert { + // + } + &.__versionControllerSave { + // + } + &.__versionControllerSubmit { + // + } + &.__versionControllerPermissions { + // + } + &.__vlm { + width: 53px; + height: 47px; + } + &.__vsp { + width: 53px; + height: 47px; + } + &.__zip { + width: 29px; + height: 23px; + } +} + +.svg-icon-missing { + @include body-2; + @include font-error; +} diff --git a/components/input/_input.scss b/components/input/_input.scss new file mode 100644 index 0000000..a84d312 --- /dev/null +++ b/components/input/_input.scss @@ -0,0 +1,78 @@ +.sdc-input { + margin-bottom: 10px; + + .sdc-input__label { + margin-bottom: 5px; + display: block; + @include body-3-emphasis; + + &.required::before { + content: '*'; + color: $red; + margin: 0 4px 0 0; + } + } + + .sdc-input__input { + @include box-sizing; + padding: 0 10px; + height: 38px; + width: 100%;//415px; + border: solid 1px $light-gray; + border-radius:2px; + color: $dark-gray; + + &.error, &.error:focus, &.error:disabled { + border: solid 1px $red; + color: $red; + outline: none; + } + + &:read-only{ + border: none; + outline: none; + color: $text-black + } + &:-moz-read-only { /* For Firefox */ + border: none; + outline: none; + color: $text-black + } + + &:focus { + border-color: $blue; + outline: 0 none; + color: $text-black; + } + + &:disabled { + background: $light-silver; + color: $gray; + } + + &::-webkit-input-placeholder /* Chrome/Opera/Safari */ { + color: $gray; + @include base-font-italic; + } + &::-moz-placeholder /* Firefox 19+ */ { + color: $gray; + @include base-font-italic; + } + &:-moz-placeholder /* Firefox 18- */ { + color: $gray; + @include base-font-italic; + } + &:-ms-input-placeholder /* IE 10+ */ + { + color: $gray; + @include base-font-italic; + } + } + + .sdc-label__error{ + margin-top: 2px; + margin-left: 2px; + @include body-3; + } + +} \ No newline at end of file diff --git a/components/input/input-disabled.html b/components/input/input-disabled.html new file mode 100644 index 0000000..bea21b4 --- /dev/null +++ b/components/input/input-disabled.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/components/input/input-error.html b/components/input/input-error.html new file mode 100644 index 0000000..aafee55 --- /dev/null +++ b/components/input/input-error.html @@ -0,0 +1,17 @@ +
+ + +
+
+ + + + + This is the error message. + +
+
+
\ No newline at end of file diff --git a/components/input/input-number.html b/components/input/input-number.html new file mode 100644 index 0000000..59ef93c --- /dev/null +++ b/components/input/input-number.html @@ -0,0 +1,6 @@ +
+ +
+ +
+
\ No newline at end of file diff --git a/components/input/input-placeholder.html b/components/input/input-placeholder.html new file mode 100644 index 0000000..b07d75d --- /dev/null +++ b/components/input/input-placeholder.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/components/input/input-required.html b/components/input/input-required.html new file mode 100644 index 0000000..f9dbb16 --- /dev/null +++ b/components/input/input-required.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/components/input/input-view-only.html b/components/input/input-view-only.html new file mode 100644 index 0000000..4381b22 --- /dev/null +++ b/components/input/input-view-only.html @@ -0,0 +1,4 @@ +
+ + +
\ No newline at end of file diff --git a/components/input/input.html b/components/input/input.html new file mode 100644 index 0000000..ef2ba61 --- /dev/null +++ b/components/input/input.html @@ -0,0 +1,8 @@ +
+ + +
\ No newline at end of file diff --git a/components/menu/_menu.scss b/components/menu/_menu.scss new file mode 100644 index 0000000..fe32f32 --- /dev/null +++ b/components/menu/_menu.scss @@ -0,0 +1,68 @@ +.sdc-menu-list { + position: static; + @include box-sizing; + @include box-shadow(0 2px 4px 0 rgba($black, 0.3)); + @include border-radius(2px); + border: 1px solid $light-gray; + border-top: solid 3px $blue; + background: $white; + min-width: 150px; + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + + .sdc-menu-item:not(.separator) { + @include body-1; + height: 40px; + color: $dark-gray; + padding: 0 12px; + display: flex; + align-items: center; + + svg { + margin-right: 16px; + g, path { + fill: $dark-gray; + } + } + + &:hover { + &:not(.disabled) { + cursor: pointer; + &:not(.selected) { + background: $light-silver; + color: $text-black; + g, path { + fill: $dark-gray; + } + } + } + } + + &.disabled { + color: $gray; + g, path { + fill: $light-gray; + } + } + + &.selected { + background: $lighter-blue; + color: $light-blue; + g, path { + fill: $dark-gray; + } + } + } + + &.relative { + position: relative; + } + + .separator { + cursor: default; + border-top: 1px solid $silver; + margin: 0 0; + height: 0; + } +} diff --git a/components/menu/popup-menu.html b/components/menu/popup-menu.html new file mode 100644 index 0000000..f4f1cbd --- /dev/null +++ b/components/menu/popup-menu.html @@ -0,0 +1,8 @@ + +
    +
  • item 1 (selected)
  • +
  • item 2
  • +
  • +
  • item 3
  • +
  • custom action
  • +
\ No newline at end of file diff --git a/components/menu/relative-popup-menu.html b/components/menu/relative-popup-menu.html new file mode 100644 index 0000000..e5a5449 --- /dev/null +++ b/components/menu/relative-popup-menu.html @@ -0,0 +1,8 @@ +
+
    +
  • item 1 (selected)
  • +
  • item 2
  • +
  • +
  • item 3
  • +
+
\ No newline at end of file diff --git a/components/modal/_modal.scss b/components/modal/_modal.scss new file mode 100644 index 0000000..e87e516 --- /dev/null +++ b/components/modal/_modal.scss @@ -0,0 +1,194 @@ +.sdc-modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + overflow: auto; + margin: auto; + display: flex; + align-items: center; + z-index: 1001; + svg path { + fill: inherit; + } + .sdc-modal__wrapper { + @include body-1; + background: $white; + width: 100%; + @include box-shadow(0 0 4px 0 rgba(0, 0, 0, 0.50)); + color: $text-black; + display: flex; + flex-direction: column; + &.sdc-modal-type-info { + border-top: solid 6px $blue; + .sdc-modal__svg-use { + fill: $blue; + } + .svg-icon { + &.__errorCircle { + width: 30px; + height: 30px; + } + } + } + &.sdc-modal-type-alert { + border-top: solid 6px $yellow; + .sdc-modal__svg-use { + fill: $yellow; + } + .svg-icon { + &.__exclamationTriangleLine { + width: 30px; + height: 30px; + } + } + } + &.sdc-modal-type-error { + border-top: solid 6px $red; + .sdc-modal__svg-use { + fill: $red; + } + .svg-icon { + &.__error { + width: 30px; + height: 30px; + } + } + } + &.sdc-modal-type-custom { + padding: 0px; + border-top: none; + .sdc-custom__header { + @include box-sizing; + background-color: $blue; + color: $white; + height: 50px; + align-items: center; + padding-top: 0px; + .title { + color: $white; + padding-top: 0px; + @include heading-3; + } + .sdc-modal__close-button { + margin-top: 0px; + width: 16px; + height: 16px; + line-height: 16px; + .svg-icon > svg { + fill: $white; + color: $white; + } + &.disabled { + cursor: default; + .svg-icon > svg { + fill: $silver; + color: $silver; + } + } + } + .sdc-modal__close-button-svg { + width: 16px; + height: 16px; + .sdc-modal__svg-use { + fill: $white; + } + .svg-icon { + height: 16px; + width: 16px; + fill: $white; + } + } + } + .sdc-modal__content { + padding: 20px 40px; + } + } + .sdc-modal__header { + padding: 20px 20px 0 20px; + display: flex; + justify-content: space-between; + text-align: left; + height: 30px; + line-height: 30px; + .sdc-modal__icon { + margin-right: 10px; + } + .title { + @include heading-2; + flex: 1 1 auto; + color: $text-black; + } + .sdc-modal__close-button { + order: 3; + width: 14px; + height: 14px; + line-height: 14px; + cursor: pointer; + .sdc-modal__svg-use { + fill: $black; + } + &.disabled { + cursor: default; + } + } + } + .sdc-modal__content { + order: 2; + padding: 10px 60px 20px 60px; + } + .sdc-modal__footer { + order: 3; + background-color: $white; + border-top: solid 1px $silver; + padding: 10px; + display: flex; + justify-content: flex-end; + button { + margin-left: 10px; + } + } + } +} + +.modal-background { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + background-color: $black; + opacity: 0.70; + z-index: 1000; + &.show { + z-index: 1000; + opacity: 0.70; + transition: opacity .3s ease, z-index .3s; + } + &.hide { + z-index: -1; + opacity: 0; + transition: opacity .35s ease, z-index .35s; + } +} + +.xl { + width: 1200px; +} + +.l { + width: 875px; +} + +.md { + width: 650px; +} + +.sm { + width: 500px; +} + +.xsm { + width: 432px; +} diff --git a/components/modal/alert-modal.html b/components/modal/alert-modal.html new file mode 100644 index 0000000..1ed72e8 --- /dev/null +++ b/components/modal/alert-modal.html @@ -0,0 +1,45 @@ +
+
+
+
+ + + + + + + + + + + + + + + + + +
+
Title
+
+ + + + + +
+
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed risus nisl, egestas vitae erat non,pulvinar lacinia libero. Integer pulvinar pellentesque accumsan. Sed hendrerit lacus eu tempus pharetra
+ +
+
+ \ No newline at end of file diff --git a/components/modal/custom-modal.html b/components/modal/custom-modal.html new file mode 100644 index 0000000..1011ca6 --- /dev/null +++ b/components/modal/custom-modal.html @@ -0,0 +1,27 @@ +
+
+
+
Title
+
+ + + + + +
+
+
+
+
+
+
+
+
+ +
+
+ \ No newline at end of file diff --git a/components/modal/error-modal.html b/components/modal/error-modal.html new file mode 100644 index 0000000..5a3b5fb --- /dev/null +++ b/components/modal/error-modal.html @@ -0,0 +1,32 @@ +
+
+
+
+ + error + + + + + + + +
+
Title
+
+ + + + + +
+
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed risus nisl, egestas vitae erat non,pulvinar lacinia libero. Integer pulvinar pellentesque accumsan. Sed hendrerit lacus eu tempus pharetra
+ +
+
+ \ No newline at end of file diff --git a/components/modal/standard-modal.html b/components/modal/standard-modal.html new file mode 100644 index 0000000..e367900 --- /dev/null +++ b/components/modal/standard-modal.html @@ -0,0 +1,46 @@ +
+
+
+
+ + + Asset 4 + + + + + + + + +
+
+ Standard Modal +
+
+ + + + + + +
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed risus nisl, egestas vitae erat non,pulvinar lacinia libero. Integer pulvinar pellentesque accumsan. Sed hendrerit lacus eu tempus pharetra +
+ +
+
+ diff --git a/components/notification/_notification.scss b/components/notification/_notification.scss new file mode 100644 index 0000000..dafe8d4 --- /dev/null +++ b/components/notification/_notification.scss @@ -0,0 +1,59 @@ +@include mixin-keyframes-fade-in-vertically(-50px, 'keyframes-slide-notif-in'); +@include mixin-keyframes-fade-out-vertically(20px, 'keyframes-slide-notif-out'); + +.sdc-notification { + position:relative; + + .sdc-notification__wrapper { + padding: 10px; + margin-top:10px; + width: 212px; + animation: keyframes-slide-notif-in 1s; + + &.fade-out__animated { + animation: keyframes-slide-notif-out 0.8s + } + + &.type-info { + background-color: #0e90d2; } + + &.type-error { + background-color: #dd514c; } + + &.type-success { + background-color: #5eb95e; } + + &.type-warn { + background-color: #f37b1d; } + } + + .sdc-notification__content { + display:flex; + flex-direction:row; + + .sdc-notification__icon { + flex: 0 0 auto; + height: 32px; + width: 32px; + margin: 0 14px 0 7px; + background: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20viewBox%3D%220%200%2032%2032%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%2382c355%3B%7D.cls-2%7Bfill%3Anone%3Bstroke%3A%23ebf5e4%3Bstroke-miterlimit%3A10%3B%7D%3C/style%3E%3C/defs%3E%3Ctitle%3Evicon%3C/title%3E%3Cg%20id%3D%22Layer_1%22%20data-name%3D%22Layer%201%22%3E%3Ccircle%20class%3D%22cls-1%22%20cx%3D%2216%22%20cy%3D%2216%22%20r%3D%2216%22/%3E%3C/g%3E%3Cg%20id%3D%22Layer_4%22%20data-name%3D%22Layer%204%22%3E%3Cpolyline%20class%3D%22cls-2%22%20points%3D%227.46%2017.43%2015.36%2021.74%2022.54%208.57%22/%3E%3C/g%3E%3C/svg%3E') + } + + .sdc-notification__message { + flex: 1; + text-align: left; + + .sdc-notification__title { + font-size:13px; + color: white; + } + + .sdc-notification__text { + font-size: 12px; + color: white; + } + } + + } + +} diff --git a/components/notification/notification-info.html b/components/notification/notification-info.html new file mode 100644 index 0000000..6eab4c0 --- /dev/null +++ b/components/notification/notification-info.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/components/notifications-container/_notifications-container.scss b/components/notifications-container/_notifications-container.scss new file mode 100644 index 0000000..7a7a907 --- /dev/null +++ b/components/notifications-container/_notifications-container.scss @@ -0,0 +1,8 @@ +.sdc-notification-container { + + + position:absolute; + top: 10px; + right: 10px; + width: 232px; +} diff --git a/components/panel/basic-panel.html b/components/panel/basic-panel.html new file mode 100644 index 0000000..14e6461 --- /dev/null +++ b/components/panel/basic-panel.html @@ -0,0 +1,21 @@ +
+

+ Panel +

+
+ +
+
+ +
+
\ No newline at end of file diff --git a/components/panel/panel.scss b/components/panel/panel.scss new file mode 100644 index 0000000..921dd74 --- /dev/null +++ b/components/panel/panel.scss @@ -0,0 +1,8 @@ +.sdc-panel { + width: 280px; + overflow-y: auto; + height: 100%; + box-shadow: 1px 0px 4px 0px $light-gray; + background-color: $white; + padding: 0; +} \ No newline at end of file diff --git a/components/radio/_radio.scss b/components/radio/_radio.scss new file mode 100644 index 0000000..9c51846 --- /dev/null +++ b/components/radio/_radio.scss @@ -0,0 +1,69 @@ +.sdc-radio { + line-height: 14px; + + label { + position: relative; + display: block; + padding-left: 14px; + } + + .sdc-radio__input { + appearance: none; + -moz-appearance: none; + -webkit-appearance: none; + position: absolute; + z-index: -1; + opacity: 0; + + // Radio not checked + + .sdc-radio__label:before { + display: inline-block; + position: absolute; + left: 0; + top: 0; + content: ""; + width: 14px; + height: 14px; + box-sizing: content-box; + background: no-repeat url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%3E%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Cpath%20fill%3D%22%23d2d2d2%22%20d%3D%22M7%2C14%20C3.13400675%2C14%200%2C10.8659932%200%2C7%20C0%2C3.13400675%203.13400675%2C0%207%2C0%20C8.35813029%2C0%209.62592397%2C0.386776975%2010.699241%2C1.0561909%20C12.6811805%2C2.29230086%2014%2C4.49213704%2014%2C7%20C14%2C10.8659932%2010.8659932%2C14%207%2C14%20Z%22%2F%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M7%2C13%20C10.3137085%2C13%2013%2C10.3137085%2013%2C7%20C13%2C3.6862915%2010.3137085%2C1%207%2C1%20C3.6862915%2C1%201%2C3.6862915%201%2C7%20C1%2C10.3137085%203.6862915%2C13%207%2C13%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E'); + } + + // Radio disabled and not checked + &:disabled:not(:checked) { + + .sdc-radio__label:before { + background: no-repeat url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%3E%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Cpath%20fill%3D%22%23d2d2d2%22%20d%3D%22M7%2C14%20C3.13400675%2C14%200%2C10.8659932%200%2C7%20C0%2C3.13400675%203.13400675%2C0%207%2C0%20C8.35813029%2C0%209.62592397%2C0.386776975%2010.699241%2C1.0561909%20C12.6811805%2C2.29230086%2014%2C4.49213704%2014%2C7%20C14%2C10.8659932%2010.8659932%2C14%207%2C14%20Z%22%2F%3E%3Cpath%20fill%3D%22%23F2F2F2%22%20d%3D%22M7%2C13%20C10.3137085%2C13%2013%2C10.3137085%2013%2C7%20C13%2C3.6862915%2010.3137085%2C1%207%2C1%20C3.6862915%2C1%201%2C3.6862915%201%2C7%20C1%2C10.3137085%203.6862915%2C13%207%2C13%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E'); + } + } + + &:checked { + // Radio checked + + .sdc-radio__label:before { + background: no-repeat url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%3E%3Cg%20fill-rule%3D%22evenodd%22%3E%3Cpath%20fill%3D%22%23009fdb%22%20fill-rule%3D%22nonzero%22%20d%3D%22M7%2C14%20C3.13400675%2C14%200%2C10.8659932%200%2C7%20C0%2C3.13400675%203.13400675%2C0%207%2C0%20C10.8659932%2C0%2014%2C3.13400675%2014%2C7%20C14%2C10.8659932%2010.8659932%2C14%207%2C14%20Z%20M7%2C13.1764706%20C10.4111705%2C13.1764706%2013.1764706%2C10.4111705%2013.1764706%2C7%20C13.1764706%2C3.58882949%2010.4111705%2C0.823529412%207%2C0.823529412%20C3.58882949%2C0.823529412%200.823529412%2C3.58882949%200.823529412%2C7%20C0.823529412%2C10.4111705%203.58882949%2C13.1764706%207%2C13.1764706%20Z%22%2F%3E%3Ccircle%20fill%3D%22%23009fdb%22%20cx%3D%227%22%20cy%3D%227%22%20r%3D%224%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E'); + } + + // Radio disabled and checked + &:disabled { + + .sdc-radio__label:before { + background: no-repeat url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%3E%3Cg%20fill-rule%3D%22evenodd%22%3E%3Cpath%20fill%3D%22%23d2d2d2%22%20fill-rule%3D%22nonzero%22%20d%3D%22M7%2C14%20C3.13400675%2C14%200%2C10.8659932%200%2C7%20C0%2C3.13400675%203.13400675%2C0%207%2C0%20C10.8659932%2C0%2014%2C3.13400675%2014%2C7%20C14%2C10.8659932%2010.8659932%2C14%207%2C14%20Z%20M7%2C13.1764706%20C10.4111705%2C13.1764706%2013.1764706%2C10.4111705%2013.1764706%2C7%20C13.1764706%2C3.58882949%2010.4111705%2C0.823529412%207%2C0.823529412%20C3.58882949%2C0.823529412%200.823529412%2C3.58882949%200.823529412%2C7%20C0.823529412%2C10.4111705%203.58882949%2C13.1764706%207%2C13.1764706%20Z%22%2F%3E%3Ccircle%20fill%3D%22%23d2d2d2%22%20cx%3D%227%22%20cy%3D%227%22%20r%3D%224%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E'); + } + } + } + + &:not(:disabled) { + + .sdc-radio__label { + cursor: pointer; + } + } + + &:disabled { + + .sdc-radio__label { + color: $gray; + } + } + } + + .sdc-radio__label:not(:empty) { + padding-left: 14px; + @include body-1; + } +} diff --git a/components/radio/radio-checked.html b/components/radio/radio-checked.html new file mode 100644 index 0000000..78283b3 --- /dev/null +++ b/components/radio/radio-checked.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/components/radio/radio-disabled-checked.html b/components/radio/radio-disabled-checked.html new file mode 100644 index 0000000..9ba6f0c --- /dev/null +++ b/components/radio/radio-disabled-checked.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/components/radio/radio-disabled.html b/components/radio/radio-disabled.html new file mode 100644 index 0000000..332435f --- /dev/null +++ b/components/radio/radio-disabled.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/components/radio/radio-unchecked.html b/components/radio/radio-unchecked.html new file mode 100644 index 0000000..6f6a00d --- /dev/null +++ b/components/radio/radio-unchecked.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/components/radioGroup/_radioGroup.scss b/components/radioGroup/_radioGroup.scss new file mode 100644 index 0000000..6b33a79 --- /dev/null +++ b/components/radioGroup/_radioGroup.scss @@ -0,0 +1,20 @@ +.sdc-radio-group { + .sdc-radio-group__radios { + display: flex; + } + .sdc-radio-group__legend { + @include body-2-emphasis; + display: inline-block; + margin-bottom: 5px; + } + .sdc-radio__label { + @include body-1; + margin-right: 20px; + } + .vertical{ + flex-direction: column; + .sdc-radio{ + margin-bottom: 15px; + } + } +} diff --git a/components/radioGroup/radio-group-disabled.html b/components/radioGroup/radio-group-disabled.html new file mode 100644 index 0000000..3cdedc0 --- /dev/null +++ b/components/radioGroup/radio-group-disabled.html @@ -0,0 +1,13 @@ +
+ +
+
+ + +
+
+ + +
+
+
diff --git a/components/radioGroup/radio-group-no-title.html b/components/radioGroup/radio-group-no-title.html new file mode 100644 index 0000000..76217fe --- /dev/null +++ b/components/radioGroup/radio-group-no-title.html @@ -0,0 +1,12 @@ +
+
+
+ + +
+
+ + +
+
+
diff --git a/components/radioGroup/radio-group-value.html b/components/radioGroup/radio-group-value.html new file mode 100644 index 0000000..ac604a6 --- /dev/null +++ b/components/radioGroup/radio-group-value.html @@ -0,0 +1,13 @@ +
+ +
+
+ + +
+
+ + +
+
+
diff --git a/components/radioGroup/radio-group.html b/components/radioGroup/radio-group.html new file mode 100644 index 0000000..baa1444 --- /dev/null +++ b/components/radioGroup/radio-group.html @@ -0,0 +1,13 @@ +
+ +
+
+ + +
+
+ + +
+
+
diff --git a/components/search-bar/_search-bar.scss b/components/search-bar/_search-bar.scss new file mode 100644 index 0000000..3e2dfad --- /dev/null +++ b/components/search-bar/_search-bar.scss @@ -0,0 +1,61 @@ +$inputHeight: 38px; +.sdc-search-bar{ + .search-bar-container{ + display: flex; + align-items: flex-end; + .sdc-input-wrapper{ + flex-grow: 1; + .sdc-input{ + margin: 0; + .sdc-input__input{ + border-bottom-right-radius: 0; + border-top-right-radius: 0; + } + } + } + .search-button{ + width: $inputHeight; + height: $inputHeight - 2; + border: solid 1px $light-gray; + border-left: none; + background-color: $light-silver; + cursor: auto; + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; + + svg{ + height: 20px; + width: 20px; + position: relative; + top: 50%; + left: 50%; + transform: translate(-50%,-50% ); + path{ + fill: $gray; + } + } + } + + &.not-empty{ + .sdc-input__input { + border-color: $blue; + } + .search-button{ + background-color: $lighter-blue; + border-color: $blue; + cursor: pointer; + svg path{ + fill: $blue; + } + &:hover { + background-color: $light-blue; + svg{ + path{ + fill: $white; + } + } + } + } + } + } +} diff --git a/components/search-bar/search-bar-with-text.html b/components/search-bar/search-bar-with-text.html new file mode 100644 index 0000000..f623c2f --- /dev/null +++ b/components/search-bar/search-bar-with-text.html @@ -0,0 +1,17 @@ + + diff --git a/components/search-bar/search-bar.html b/components/search-bar/search-bar.html new file mode 100644 index 0000000..d1b9171 --- /dev/null +++ b/components/search-bar/search-bar.html @@ -0,0 +1,16 @@ + diff --git a/components/tabs/tabs-disabled.html b/components/tabs/tabs-disabled.html new file mode 100644 index 0000000..5e67f2a --- /dev/null +++ b/components/tabs/tabs-disabled.html @@ -0,0 +1,8 @@ +
+
    + + + +
+
This is the active tab content
+
diff --git a/components/tabs/tabs-header.html b/components/tabs/tabs-header.html new file mode 100644 index 0000000..622dc32 --- /dev/null +++ b/components/tabs/tabs-header.html @@ -0,0 +1,8 @@ +
+
    + + + +
+
This is the active tab content
+
diff --git a/components/tabs/tabs-menu.html b/components/tabs/tabs-menu.html new file mode 100644 index 0000000..753886f --- /dev/null +++ b/components/tabs/tabs-menu.html @@ -0,0 +1,8 @@ +
+
    + + + +
+
This is the active tab content
+
diff --git a/components/tabs/tabs.scss b/components/tabs/tabs.scss new file mode 100644 index 0000000..f5df0ba --- /dev/null +++ b/components/tabs/tabs.scss @@ -0,0 +1,35 @@ +.sdc-tabs { + .sdc-tab { + display: inline-block; + cursor: pointer; + text-transform: capitalize; + color: $dark-gray; + padding: 0px 10px 7px 10px; + margin-right: 20px; + &:last-child { + margin-right: 0; + } + &.sdc-tab-active { + color: $blue; + border-bottom: 2px solid $blue; + } + &[disabled] { + opacity: 0.3; + cursor: default; + } + } + &.sdc-tabs-header { + .sdc-tab { + @include heading-2; + } + } + &.sdc-tabs-menu { + .sdc-tab { + @include body-1; + padding: 0px 10px 4px 10px; + } + } + .sdc-tab-content { + margin-top: 30px; + } +} diff --git a/components/tag-cloud/_tag-cloud.scss b/components/tag-cloud/_tag-cloud.scss new file mode 100644 index 0000000..6a94a10 --- /dev/null +++ b/components/tag-cloud/_tag-cloud.scss @@ -0,0 +1,116 @@ +.sdc-tag-cloud-new-item-field{ + display: flex; + align-items: flex-end; + sdc-input{ + flex-grow: 1; + .sdc-input{ + margin-bottom: 0; + .sdc-input__input{ + border-bottom-right-radius: 0; + border-top-right-radius: 0; + } + } + } + .not-empty{ + .sdc-input__input { + border-color: $blue; + } + + } + .error{ + .sdc-input__input { + border: solid 1px $red; + color: $red; + outline: none; + } + } + .add-button{ + cursor: pointer; + height: 36px; + width: 36px; + text-align: center; + background-color: $lighter-blue; + border: solid 1px $blue; + border-left: none; + border-top-right-radius: 2px; + &.disabled{ + cursor: default; + background-color: $light-silver; + border-color: $light-gray; + .plus-icon svg g{ + fill: $gray; + } + } + .plus-icon{ + line-height: 46px; + svg{ + height: 20px; + width: 20px; + g{ + fill: $blue; + } + } + } + } + + &.not-empty{ + .add-button:hover{ + background-color: $light-blue; + svg{ + g{ + fill: $white; + } + } + } + } +} + +.sdc-list-container{ + height: 120px; + overflow-y: auto; + border: solid 1px $light-gray; + border-top: none; + border-radius: 2px; + background-color: $white; + padding: 10px 10px 0 0; + .sdc-tag-item{ + padding: 0 0 10px 10px; + display: inline-block; + .tag-item{ + min-width: 100px; + background-color: $white; + border: solid 1px $light-gray; + border-radius: 20px; + height: 28px; + line-height: 28px; + padding: 0 10px; + color: $text-black; + @include body-3; + &.view-only{ + border-color: $silver; + } + .delete-item{ + cursor: pointer; + padding-left: 10px; + float: right; + svg{ + height: 12px; + width: 12px; + margin-top: 8px; + g{ + fill: $dark-gray; + } + } + } + &:hover:not(.view-only){ + background-color: $lighter-blue; + border-color: $lighter-blue; + .delete-item{ + svg g{ + fill: $blue; + } + } + } + } + } +} diff --git a/components/tag-cloud/disabled-list.html b/components/tag-cloud/disabled-list.html new file mode 100644 index 0000000..6091525 --- /dev/null +++ b/components/tag-cloud/disabled-list.html @@ -0,0 +1,31 @@ +
+
+ + +
+
+ + + + + + + + + + +
+
+
+
+
+ aaa +
+
+ +
+
+ bbb +
+
+
diff --git a/components/tag-cloud/list-with-active-add-button.html b/components/tag-cloud/list-with-active-add-button.html new file mode 100644 index 0000000..8bde59f --- /dev/null +++ b/components/tag-cloud/list-with-active-add-button.html @@ -0,0 +1,50 @@ +
+
+ + +
+
+ + + + + + + + + + +
+
+
+
+
+ aaa + + + + + + + + + + +
+
+
+
+ bbb + + + + + + + + + + +
+
+
diff --git a/components/tag-cloud/list-with-some-read-only-items.html b/components/tag-cloud/list-with-some-read-only-items.html new file mode 100644 index 0000000..0db7a07 --- /dev/null +++ b/components/tag-cloud/list-with-some-read-only-items.html @@ -0,0 +1,61 @@ +
+
+ + +
+ +
+ + + + + + + + + + +
+
+
+
+
+ aaa + + + + + + + + + + +
+
+
+
+ bbb +
+
+
+
+ ccc + + + + + + + + + + +
+
+ +
+ ddd +
+
+
diff --git a/components/tag-cloud/list-with-unique-error.html b/components/tag-cloud/list-with-unique-error.html new file mode 100644 index 0000000..14de80c --- /dev/null +++ b/components/tag-cloud/list-with-unique-error.html @@ -0,0 +1,52 @@ +
+
+ + +
+
+ + + + + + + + + + +
+
+
+
+
+ aaa + + + + + + + + + + +
+
+
+
+ bbb + + + + + + + + + + +
+
+
+
This value is already in the list
+ diff --git a/components/tag-cloud/simple-list.html b/components/tag-cloud/simple-list.html new file mode 100644 index 0000000..075751f --- /dev/null +++ b/components/tag-cloud/simple-list.html @@ -0,0 +1,50 @@ +
+
+ + +
+
+ + + + + + + + + + +
+
+
+
+
+ aaa + + + + + + + + + + +
+
+
+
+ bbb + + + + + + + + + + +
+
+
diff --git a/components/tile/_tile.scss b/components/tile/_tile.scss new file mode 100644 index 0000000..80629cf --- /dev/null +++ b/components/tile/_tile.scss @@ -0,0 +1,172 @@ +.sdc-tile { + $tile-full-width: 204px; + $tile-full-height: 204px; + $tile-padding: 10px; + + $header-height: $body-font-1; + $footer-height: 23px; + $content-height: 100%; + + width: $tile-full-width; + height: $tile-full-height; + + padding: $tile-padding; + border: 1px solid $silver; + @include border-radius(2px); + @include box-sizing; + + display: flex; + flex-direction: column; + justify-content: space-between; + + position: relative; + cursor: pointer; + user-select: none; + + background: $white; + color: $text-black; + fill: $dark-gray; + transition: box-shadow 0.3s ease-in-out; + @include body-3; + @include box-shadow(0 1px 4px 0 rgba($text-black, 0.06)); + + &:hover { + border-color: $light-gray; + @include box-shadow( 0 10px 30px 0 rgba($text-black, 0.25)); + } + + .blue { + color: $blue; + fill: $blue; + } + + .purple { + color: $purple; + fill: $purple; + } + + .centered { + &.sdc-tile-footer, .sdc-tile-info-line { + text-align: center; + } + } + + .sdc-tile-header { + height: 1.1em; + line-height: 1em; + @include ellipsis($display: block); + + font-size: $body-font-1; + text-transform: uppercase; + } + + .sdc-tile-content { + display: flex; + flex-direction: column; + justify-content: space-between; + height: $content-height; + + .sdc-tile-content-icon { + margin-top: 27px; + text-align: center; + + svg { + width: 50px; + height: 50px; + + &.__vsp { + width: 60px; + height: 40px; + } + &.__vlm { + width: 45px; + height: 53px; + } + &.__vendor { + width: 53px; + height: 47px; + } + } + } + + .sdc-tile-content-info { + display: flex; + flex-direction: column; + + .sdc-tile-info-line { + text-transform: capitalize; + @include ellipsis($display: inline-block); + + button { + height: 1.67em; + width: initial; + margin-bottom: 5px; + margin-top: 1px; + padding: 0 8px; + font-size: inherit; + } + &.supertitle { + height: 1.1em; + line-height: 1.2; + color: $gray; + } + &.title { + height: 1.6em; + line-height: 1.8; + color: $text-black; + @include heading-5; + } + &.subtitle { + height: 1.5em; + line-height: 1.6; + color: $dark-gray; + } + &:last-child { + margin-bottom: 4px; + } + } + } + } + + .sdc-tile-footer { + height: $footer-height; + @include box-sizing; + + border-top: 1px solid $silver; + padding-top: 8px; + + color: $dark-gray; + fill: $dark-gray; + text-transform: capitalize; + + display: flex; + align-items: center; + justify-content: space-between; + + .sdc-tile-footer-cell { + &:first-child { + @include ellipsis; + } + svg{ + width: 20px; + height: 12px; + } + button { + width: initial; + height: initial; + @include body-2; + + .svg-icon-wrapper { + .svg-icon-label { + font-size: inherit; + } + svg { + width: 9px; + height: 9px; + } + } + } + } + + } +} diff --git a/components/tile/tile-without-footer.html b/components/tile/tile-without-footer.html new file mode 100644 index 0000000..514a19e --- /dev/null +++ b/components/tile/tile-without-footer.html @@ -0,0 +1,14 @@ +
+
Header
+
+
+
+ +
+
+ +
+
diff --git a/components/tile/vendor-tile.html b/components/tile/vendor-tile.html new file mode 100644 index 0000000..ede3fbf --- /dev/null +++ b/components/tile/vendor-tile.html @@ -0,0 +1,26 @@ +
+
+
+
+
+ +
+
+ +
+ +
diff --git a/components/tile/vfc-tile.html b/components/tile/vfc-tile.html new file mode 100644 index 0000000..7b4753e --- /dev/null +++ b/components/tile/vfc-tile.html @@ -0,0 +1,17 @@ +
+
VFC
+
+
+
+ +
+
+ +
+ +
diff --git a/components/tile/vlm-tile.html b/components/tile/vlm-tile.html new file mode 100644 index 0000000..be629a1 --- /dev/null +++ b/components/tile/vlm-tile.html @@ -0,0 +1,22 @@ +
+
VLM
+
+
+
+ +
+
+ +
+ +
diff --git a/components/tile/vsp-tile.html b/components/tile/vsp-tile.html new file mode 100644 index 0000000..1a3dc1d --- /dev/null +++ b/components/tile/vsp-tile.html @@ -0,0 +1,17 @@ +
+
VSP
+
+
+
+ +
+
+ +
+ +
diff --git a/components/tooltip/_tooltip.scss b/components/tooltip/_tooltip.scss new file mode 100644 index 0000000..3f255ed --- /dev/null +++ b/components/tooltip/_tooltip.scss @@ -0,0 +1,124 @@ +/* Tooltip animation */ +@keyframes animation-fade-in-from-bottom { + from { + transform: translateY(10px); + opacity: 0; + } + to { + transform: translateY(0px); + opacity: 1; + } +} + +/* Tooltop styles */ +.sdc-tooltip { + @include base-font-regular; + line-height: 14px; + font-size: 12px; + max-width: 223px; + background-color: $black; + color: $white; + text-align: left; + border-radius: 2px; + padding: 5px 11px; + position: absolute; + z-index: 1000; + display: block; + opacity: 0; + transition: opacity 300ms; + border: solid 1px $black; + animation: animation-fade-in-from-bottom .75s ease-in-out; /* tooltip animation */ +} + +.sdc-tooltip-show { + opacity: 1; +} + +.sdc-tooltip::after { + content: ""; + position: absolute; + border-style: solid; +} + +.sdc-tooltip-top::after { + top: 100%; + left: 10px; + margin-left: -5px; + border-width: 5px; + border-color: $black transparent transparent transparent; +} +.sdc-tooltip-top-right__bottom::after { + right: 10px; + left:auto; +} +.sdc-tooltip-top-center__middle::after { + left: 50%; +} + +.sdc-tooltip-bottom::after { + bottom: 100%; + left: 10px; + margin-left: -5px; + border-width: 5px; + border-color: transparent transparent $black transparent; +} +.sdc-tooltip-bottom-right__bottom::after { + right: 10px; + left:auto; +} +.sdc-tooltip-bottom-center__middle::after { + left: 50%; +} + +.sdc-tooltip-left::after { + top: 10px; /*50%;*/ + left: 100%; + margin-top: -5px; + border-width: 5px; + border-color: transparent transparent transparent $black; +} +.sdc-tooltip-left-right__bottom::after { + bottom: 10px; + top: auto; +} +.sdc-tooltip-left-center__middle::after { + top: 50% +} + +.sdc-tooltip-right::after { + top: 10px; + right: 100%; + margin-top: -5px; + border-width: 5px; + border-color: transparent $black transparent transparent; +} +.sdc-tooltip-right-right__bottom::after { + bottom: 10px; + top: auto; +} +.sdc-tooltip-right-center__middle::after { + top: 50% +} + +/* Tooltip template */ +.sdc-tooltip-template-title { + @include base-font-regular; + font-size: 12px; + font-weight: normal; + background-color: $black; + color: $white; /* silver */ +} + +.sdc-tooltip-template-big-title { + font-size: 20px; + line-height: 24px; +} + +.sdc-tooltip-template-content { + @include base-font-regular; + font-size: 12px; + font-weight: normal; + background-color: $black; + color: $white; + line-height: 14px; +} diff --git a/components/validation/_validation.scss b/components/validation/_validation.scss new file mode 100644 index 0000000..771a242 --- /dev/null +++ b/components/validation/_validation.scss @@ -0,0 +1,9 @@ +.sdc-validation { + + font-size: $body-font-4; + display: flex; + flex-direction: column; + justify-content: space-between; + margin-top: 10px; + +} diff --git a/demo/.gitignore b/demo/.gitignore new file mode 100644 index 0000000..7788a08 --- /dev/null +++ b/demo/.gitignore @@ -0,0 +1,7 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +/dist +/tmp +/node_modules +npm-debug.log* +Thumbs.db diff --git a/demo/README.md b/demo/README.md new file mode 100644 index 0000000..6a478d8 --- /dev/null +++ b/demo/README.md @@ -0,0 +1,35 @@ +# Local DEMO + +### How to compile +Go to root folder (sdc-ui) and run: `npm run build` +This will compile all scss files and will copy generated style.css and assets folder to gen folder inside demo project. + +### How to run +Go to demo and run: `npm start` +This will open the default browser at port 2900. + +### How to build component HTML for DEMO +
    +
  • Create new HTML file for the component and put it in components folder
  • +
  • In the top of the HTML add
    Name of component
  • +
  • Inside the HTML for each type of component add header

    sub component name

  • +
  • + In order to show the HTML of the component to the user, add data-code attribute to the outerHTML of the component. + This component outerHTML will be copied and will be shown highlighted below the component. +
    You can also use data-code-ref and data-code-id where data-code-ref is a reference to the + component data-code-id, the data-code-ref will be replaced by highlighted outerHTML of the component. +
  • +
+For more info see example in tiles.html file + +### How add the new component to router for DEMO +In index.html file (under id main-navigation) add new
  • with id equal to HTML file you created above +
    Example: + diff --git a/demo/assests/README.md b/demo/assests/README.md new file mode 100644 index 0000000..5f2681b --- /dev/null +++ b/demo/assests/README.md @@ -0,0 +1,9 @@ + +# Folder Structure + +### icons +Contains `svg` icons **ONLY** + + +### images +Contains large images (if any...) diff --git a/demo/assests/icons/empty.txt b/demo/assests/icons/empty.txt new file mode 100644 index 0000000..e69de29 diff --git a/demo/assests/icons/locked.svg b/demo/assests/icons/locked.svg new file mode 100644 index 0000000..9785f8d --- /dev/null +++ b/demo/assests/icons/locked.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/assests/icons/plus.svg b/demo/assests/icons/plus.svg new file mode 100644 index 0000000..36f3486 --- /dev/null +++ b/demo/assests/icons/plus.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/demo/assests/icons/unlocked.svg b/demo/assests/icons/unlocked.svg new file mode 100644 index 0000000..6d94a94 --- /dev/null +++ b/demo/assests/icons/unlocked.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/assests/icons/vendor.svg b/demo/assests/icons/vendor.svg new file mode 100644 index 0000000..a3b8f5f --- /dev/null +++ b/demo/assests/icons/vendor.svg @@ -0,0 +1 @@ +vendor diff --git a/demo/assests/icons/vlm.svg b/demo/assests/icons/vlm.svg new file mode 100644 index 0000000..79b4625 --- /dev/null +++ b/demo/assests/icons/vlm.svg @@ -0,0 +1 @@ +vlm_new_icon \ No newline at end of file diff --git a/demo/assests/icons/vsp.svg b/demo/assests/icons/vsp.svg new file mode 100644 index 0000000..344755c --- /dev/null +++ b/demo/assests/icons/vsp.svg @@ -0,0 +1 @@ +vsp_new_icon diff --git a/demo/assests/images/empty.txt b/demo/assests/images/empty.txt new file mode 100644 index 0000000..e69de29 diff --git a/demo/components/button.html b/demo/components/button.html new file mode 100644 index 0000000..5a68bb5 --- /dev/null +++ b/demo/components/button.html @@ -0,0 +1,15 @@ +
    Button
    + +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • +
    \ No newline at end of file diff --git a/demo/components/colors.html b/demo/components/colors.html new file mode 100644 index 0000000..a9bb9f9 --- /dev/null +++ b/demo/components/colors.html @@ -0,0 +1,26 @@ +
    Colors
    +

    + List of supported colors. All colors are background colors. +

    +
      +
    • sdc-bc-blue
    • +
    • sdc-bc-dark-blue
    • +
    • sdc-bc-light-blue
    • +
    • sdc-bc-green
    • +
    • sdc-bc-dark-green
    • +
    • sdc-bc-light-green
    • +
    • sdc-bc-orange
    • +
    • sdc-bc-yellow
    • +
    • sdc-bc-dark-purple
    • +
    • sdc-bc-purple
    • +
    • sdc-bc-light-purple
    • +
    • sdc-bc-black
    • +
    • sdc-bc-dark-gray
    • +
    • sdc-bc-gray
    • +
    • sdc-bc-light-gray
    • +
    • sdc-bc-white
    • +
    +

    Example of usage:

    + + <div class='sdc-bc-blue'>sdc-bc-blue</div> + \ No newline at end of file diff --git a/demo/components/tiles-generic.html b/demo/components/tiles-generic.html new file mode 100644 index 0000000..3d03909 --- /dev/null +++ b/demo/components/tiles-generic.html @@ -0,0 +1,106 @@ +
    Tiles Generic
    +

    + Tile do not have size, you can define the size in the wrapping container, or adding them + to css in your project. +
    Note: specific tiles like sdc-tile-catalog has specifc width & height. +
    Tiles content has overflow auto, so if the content overflow a scroll will appear. +

    + +

    Basic tile

    +

    Tile without width or height

    +
    +
    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec volutpat, lorem at vulputate sollicitudin, neque magna hendrerit diam, nec sagittis eros libero ut dolor. Donec tincidunt, elit nec vestibulum tristique, nulla tellus aliquam eros, at rutrum massa purus quis ipsum. In aliquet non augue quis condimentum. Praesent efficitur tellus quis mauris auctor, vel semper erat vulputate. Mauris rhoncus nunc et ante dapibus, id luctus urna sodales. Proin eu augue efficitur ligula tincidunt placerat. Suspendisse potenti. +
    +
    + +

    Tile with header & footer

    +

    + Note: The background-color is just for showing header, content & footer sections. + This is done using inline styles which should not be included in the final component. +
    The content will resize automaticly his height. +

    +
    +
    top
    +
    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec volutpat, lorem at vulputate sollicitudin, neque magna hendrerit diam, nec sagittis eros libero ut dolor. Donec tincidunt, elit nec vestibulum tristique, nulla tellus aliquam eros, at rutrum massa purus quis ipsum. In aliquet non augue quis condimentum. Praesent efficitur tellus quis mauris auctor, vel semper erat vulputate. Mauris rhoncus nunc et ante dapibus, id luctus urna sodales. Proin eu augue efficitur ligula tincidunt placerat. Suspendisse potenti. +
    + +
    + +

    Catalog tile (VSP)

    +

    + Specific tile of SDC. +
    Note: The differences betweeb the tiles is the color (diffrent class) and the icons. + The div with class='sdc-tile-content-info-vendor-name' is not needed in the second tile. +

    + +
    + +
    +
    +
    + vsp +
    +
    +
    +
    + + + +
    + +
    + +
    + +
    +
    +
    + vlm +
    +
    +
    +
    + + + +
    + +
    + +
    + +
    + +
    + diff --git a/demo/components/tiles.html b/demo/components/tiles.html new file mode 100644 index 0000000..3519817 --- /dev/null +++ b/demo/components/tiles.html @@ -0,0 +1,105 @@ +
    Tiles
    + +

    VSP Tile

    +

    + Example +

    +
    +
    +
    VSP
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    vlm
    +
    +
    +
    lilach vsp
    +
    +
    +
    V 0.1
    +
    +
    +
    +
    Checked Out
    +
    +
    + + + +
    +
    +
    +
    +
    + +

    VLM Tile

    +
    +
    +
    VLM
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    vlm
    +
    +
    +
    V 0.2
    +
    +
    +
    +
    Checked In
    +
    +
    + + + +
    +
    +
    +
    +
    + +

    Vendor Tile

    +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    vlm
    +
    1 VSPs
    +
    +
    +
    + + + +
    +    Create new VSP +
    +
    +
    +
    diff --git a/demo/index.css b/demo/index.css new file mode 100644 index 0000000..ef71773 --- /dev/null +++ b/demo/index.css @@ -0,0 +1,178 @@ +/* GENERAL */ +body { + margin: 0; + color: #333333; + /*font-family: 'Open Sans Condensed';*/ + font-family: 'Overlock'; + font-size: 16px; + display: flex; + flex-wrap: wrap; + height: auto; +} + +h1 { font-size: 1.5em; } +h2 { font-size: 1.3em; } +h3 { font-size: 1.2em; } +h1, h2, h3 { margin: 2em 0 0.5em 0; color: #009fdb; } +p { margin: 6px 0; } + +/* LAYOUT */ +header { + order: 1; + width: 100%; + background-color: #ffffff; +} + +footer { + order: 4; + width: 100%; +} + +#main-navigation { + order: 2; + width: 100%; +} + +#main-content { + order: 3; + width: 100%; +} + +/* HEADER */ +header { + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.33); + height: 50px; + line-height: 50px; + z-index: 9; + display: flex; + flex-direction: row; + padding: 0 1vw; +} + +header .logo { + font-size: 22px; + font-weight: 700; +} + +header .title { + font-size: 20px; + margin-left: 2vw; +} + +/* FOOTER */ +footer { + height: 50px; + line-height: 50px; + z-index: 6; + display: flex; + flex-direction: row; + padding: 0 1vw; + border-top: solid 1px #dddddd; +} + +/* MAIN NAVIGATION */ +#main-navigation { + align-content: stretch; + z-index: 8; + box-shadow: 2px 0 0 0 rgba(68,68,68,0.2); +} + +#main-navigation .title { + font-size: 20px; + font-weight: 700; + background-color: #ffffff; + color: #009fdb; + border-bottom: 1px solid #d2d2d2; + height: 30px; + line-height: 30px; + padding: 0.3em; +} + +#main-navigation ul { + padding: 0; +} + +#main-navigation ul li { + background-color: #f8f8f8; + border-bottom: 1px solid #d2d2d2; + height: 30px; + line-height: 30px; + cursor: pointer; + padding: 0.3em; +} + +#main-navigation ul li.selected { + background-color: #009fdb; + color: #ffffff; +} + +/* MAIN CONTENT */ +#main-content { + -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */ + -moz-box-sizing: border-box; /* Firefox, other Gecko */ + box-sizing: border-box; /* Opera/IE 8+ */ + align-content: stretch; + padding: 2vh 2vw; + z-index: 7; +} + +/* RESPONSIVE */ +@media screen and (min-width: 800px) { + #main-navigation { width: 20%; } + #main-content { width: 80%; } +} + +/* OTHERS */ +.component { + background-color: #009fdb; + color: #ffffff; + font-size: 20px; + padding: 0.5em 1em; + margin: 0 0 0.5em 0; +} + +pre { + padding: 1em; + display: block; + margin: 1em 0; + font-family: 'PT Sans Narrow'; + font-size: 1em; +} + +/* SPECIFIC COMPONENT - TILE */ +.sdc-tile-showcase-tiles { + display: flex; + flex-direction: row; +} +.sdc-tile-showcase-tiles > div.sdc-tile-catalog { + margin: 0 5px 5px 5px; +} +.sdc-tile-showcase-tiles > div.sdc-tile-catalog:first-child { margin-left: 0; } +.sdc-tile-showcase-tiles > div.sdc-tile-catalog:last-child { margin-right: 0; } + +/* SPECIFIC COMPONENT - COLORS */ +.sdc-colors-showcase li { + padding: 1vw; + margin: 1vw; +} + +/* SPECIFIC COMPONENTS - BUTTONS */ +.buttons-showcase li { + display: flex; + flex-direction: column; + margin-top: 2vh; +} +.buttons-showcase li pre { + flex-grow: 1; + margin: 1vw 0 0.5vw 0; +} + +@media screen and (min-width: 600px) { + .buttons-showcase li { + margin-top: 0; + flex-direction: row; + } + .buttons-showcase li pre { + margin: 0.5vw 0 0.5vw 2vw; + } +} diff --git a/demo/index.html b/demo/index.html new file mode 100644 index 0000000..f3b4cb4 --- /dev/null +++ b/demo/index.html @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + SDC-UI Show case + + + +
    + +
    Show case
    +
    + + + +
    + +
    + Copywrite 2017. All rights reserved. +
    + + + + diff --git a/demo/index.js b/demo/index.js new file mode 100644 index 0000000..1c9a4bb --- /dev/null +++ b/demo/index.js @@ -0,0 +1,84 @@ +$( document ).ready(function() { + + initNavigation(); + registerEvents(); + doAfterComponentHtmlLoaded(); + +}); + +var initNavigation = function() { + + // Set default page + var page = window.location.hash; + if (page === '') { + page = "button"; + window.location.hash = page; + } + + // Remove # at the begining + if (page.indexOf('#')!==-1){ + page = page.substr(1); + } + console.log("page: " + page); + + // Upload the content + $("#main-content").load( "./components/" + page + ".html" ); + + // Set selected in navigation + $('#main-navigation #' + page).addClass('selected'); + +}; + +/** + * Simple router to load components HTML based on id. + */ +var registerEvents = function() { + $("#main-navigation li").click(function(e){ + page = e.target.id; + $("#main-content").load( "./components/" + page + ".html", function() { + doAfterComponentHtmlLoaded(); + }); + window.location.hash = page; + $('#main-navigation li').removeClass('selected'); + $('#main-navigation #' + page).addClass('selected'); + }); +}; + +/** + * Wait for component to load before applying javascript actions on it. + */ +var doAfterComponentHtmlLoaded = function() { + window.setTimeout(function() { + // Build code text to show to user + buildCode(); + + // highlight the code + hljs.initHighlightingOnLoad(); + },1000); +}; + +/** + * Build HTML code automaticly when attribute data-code exists + * For example: + * + * Will add the HTML of this (highlighted) button below the button. + */ +var buildCode = function() { + $('[data-code]').each(function(index, element){ + var result = $(element).removeAttr('data-code')[0].outerHTML; + var resultEncoded = $('
    ').text(result).html(); + var newElement = $('
    ' + resultEncoded + '
    ')[0]; + $(element).after(newElement); + hljs.highlightBlock(newElement); + }); + + $('[data-code-id]').each(function(index, element){ + var attVlue = $(element).attr('data-code-id'); + var codeHere = $('[data-code-ref='+attVlue+']'); + var result = $(element).removeAttr('data-code-id')[0].outerHTML; + var resultEncoded = $('
    ').text(result).html(); + var newElement = $('
    ' + resultEncoded + '
    ')[0]; + codeHere.replaceWith(newElement); + hljs.highlightBlock(newElement); + }); +}; diff --git a/demo/package.json b/demo/package.json new file mode 100644 index 0000000..40a7b7d --- /dev/null +++ b/demo/package.json @@ -0,0 +1,23 @@ +{ + "name": "onap-sdc", + "preferGlobal": true, + "version": "1.0.0", + "author": "Onap Sdc ", + "description": "Styles and UI components for ONAP SDC", + "license": "MIT", + "engines": { + "node": ">=0.10" + }, + "scripts": { + "start": "watch-http-server ./ -a 127.0.0.1 -p 2900 -o" + }, + "dependencies": { + "express": "~3.4.7", + "http-server": "^0.9.0" + }, + "devDependencies": { + "node-sass": "^4.5.2", + "nodemon": "^1.11.0", + "watch-http-server": "^0.7.6" + } +} diff --git a/designs/README.md b/designs/README.md new file mode 100644 index 0000000..5de4d30 --- /dev/null +++ b/designs/README.md @@ -0,0 +1,3 @@ +# Designs + +Reference folder for graphics and designs diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..51e3f70 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,30 @@ +module.exports = { + moduleFileExtensions: [ + 'js', + 'html' + ], + globals: { + ICON_PATH: './' + }, + transform: { + '^.+\\.js$': 'babel-jest', + '^.+\\.html$': '/test/react/utils/htmlLoader.js' + }, + moduleNameMapper: { + '^.+\\.svg$': '/test/react/utils/svgMock.js' + }, + coveragePathIgnorePatterns: [ + '/src/react/index.js', + '/src/index.js' + ], + testRegex: '/test/react/.*.(spec|test)\\.jsx?$', + testPathIgnorePatterns: [ + '/node_modules/', + ], + collectCoverageFrom: [ + 'src/**/*.{js,jsx}' + ], + coverageReporters: [ + 'lcov' + ] +}; diff --git a/json-typing.d.ts b/json-typing.d.ts new file mode 100644 index 0000000..4b3471f --- /dev/null +++ b/json-typing.d.ts @@ -0,0 +1,4 @@ +declare module "*.json" { + const value: any; + export default value; +} diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..bc35fb7 --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,87 @@ +/**************************************************************************************** + * Karma configuration file + ***************************************************************************************/ + +module.exports = function (config) { + config.set({ + basePath:'', + files:[ + 'karma.entry.js', + ], + + /** + * Enable / disable watching file and executing tests whenever any file changes + */ + autoWatch: true, + + /** + * Here, we tell Karma we'll be using Jasmine + */ + frameworks:['jasmine'], + + /** + * Level of logging + * possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + */ + logLevel: config.LOG_INFO, + + /** + * Currently we have the following launchers: Firefox, Chrome, PhantomJS + * Available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + */ + browsers: ['Firefox', 'PhantomJS'], + + /** + * Tells PhantomJS to shut down if Karma throws a ResourceError, If we didn't, PhantomJS might not shut down, + * and this would eat away at our system resources. + */ + phantomJsLauncher: { + exitOnResourceError: true + }, + + /** + * We tell Karma to run a list of preprocessors on our karma.entry.js file + */ + preprocessors: { + 'karma.entry.js': ['webpack', 'sourcemap'] + }, + + + /** + * dots: Tells karma to use dots reporter, which outputs a single dot for each test instead of descriptive message. + * kjhtml - Karma Jasmine HTML Reporter. Reporter that dynamically shows tests results at debug.html page. + */ + reporters: ['dots', 'kjhtml'], + + /** + * leave Jasmine Spec Runner output visible in browser + */ + client: { + clearContext: false + }, + + /** + * Keeps karma running after it completed it's tests for later rerun. + */ + singleRun: false, + + failOnEmptyTestSuite:false, + + /** + * Load webpack test configuration file + */ + webpack: require('./webpack/webpack.test.js'), + + webpackMiddleware: { + noInfo: true + }, + + /** + * Hide webpack bundle information messages + */ + webpackServer: { + noInfo: true + } + }) +}; + diff --git a/karma.entry.js b/karma.entry.js new file mode 100644 index 0000000..acd16cc --- /dev/null +++ b/karma.entry.js @@ -0,0 +1,66 @@ +/**************************************************************************************** + * Karma entry file. + * Starting point for pulling in our test and application files when using karma. + ***************************************************************************************/ + +/** + * Load dependencies: + * - core-js/es6 - Javascript polyfills support es6 code on unsupported browsers + * - reflect-metadata - support for decorators and reflection. + * - Zone library - Monkey patch all asynchronous actions and jasmine so changes could be detected + * as well as long stack traces (To get the root cause of errors). + */ + +require('core-js/es6'); + +require('reflect-metadata'); + +require('zone.js/dist/zone'); +require('zone.js/dist/async-test'); +require('zone.js/dist/fake-async-test'); +require('zone.js/dist/long-stack-trace-zone'); // Gives us a long backward trace of actions and errors +require('zone.js/dist/proxy'); +require('zone.js/dist/sync-test'); +require('zone.js/dist/jasmine-patch'); + +/** + * RxJS + */ +require('rxjs/Rx'); + + +/** + * Angular testing libraries dependencies + */ +const browserTesting = require('@angular/platform-browser-dynamic/testing'); +const coreTesting = require('@angular/core/testing'); + +coreTesting.TestBed.initTestEnvironment( + browserTesting.BrowserDynamicTestingModule, + browserTesting.platformBrowserDynamicTesting() +); + +/** + * Webpack method. requires all the files and subdirectories of a pattern. + * Checkout {@link https://webpack.github.io/docs/context.html} + */ +const context = require.context('./src/angular/', true, /\.spec\.ts$/); + +/** + * Get all the files, for each file, call the context function + * that will require the file and load it up here. Context will + * loop and require those spec files here + */ +function requireAll(requireContext) { + return requireContext.keys().map(requireContext); +} + +requireAll(context); + +/** + * These lines are the Jasmine setup . We'll make sure that we get full stack traces when we have a + * problem and that Jasmine uses two seconds as its default timeout. + * The timeout is used when we test asynchronous processes. If we don't set this properly, some of our tests could hang forever. + */ +Error.stackTraceLimit = Infinity; +jasmine.DEFAULT_TIMEOUT_INTERVAL = 2000; diff --git a/ng2-component-lab.webpack.config.js b/ng2-component-lab.webpack.config.js new file mode 100644 index 0000000..d2f0551 --- /dev/null +++ b/ng2-component-lab.webpack.config.js @@ -0,0 +1,62 @@ +var path = require('path'); +var HtmlReplaceWebpackPlugin = require('html-replace-webpack-plugin') +var baseHref = process.env.NODE_ENV === 'build' ? '' : '' + +var webpackConfig = { + + devtool: 'source-map', + + output: { + path: path.resolve('.out/angular'), + publicPath: '' + }, + + plugins: [ + new HtmlReplaceWebpackPlugin([{ + pattern: '', + replacement: baseHref + }]) + ], + + module: { + rules: [ + // .ts files for TypeScript + { + test: /.ts$/, + use: [ + 'awesome-typescript-loader', + 'angular2-template-loader', + 'angular2-router-loader' + ] + }, + { test: /\.html$/, loader: "html-loader" }, + { + test: /.scss$/, + use: ['style-loader', 'css-loader', 'sass-loader'], + include: path.resolve(__dirname, '../') + }, + ] + }, + + resolve: { + extensions: ['.ts', '.js'], + modules: [path.resolve(__dirname, 'node_modules')], + alias: { + root: path.resolve(__dirname), + app: path.resolve(__dirname, 'src') + } + }, + + node: { + global: true, + crypto: 'empty', + __dirname: true, + __filename: true, + process: true, + Buffer: false, + clearImmediate: false, + setImmediate: false + } +}; + +module.exports = webpackConfig; diff --git a/package.json b/package.json new file mode 100644 index 0000000..8495da4 --- /dev/null +++ b/package.json @@ -0,0 +1,133 @@ +{ + "name": "sdc-ui", + "version": "1.6.42", + "description": "This project aims to create a unified UI styled components for multiple development teams who work on the same web-based applications.", + "scripts": { + "test": "jest && npm run karma-travis", + "test-dev": "jest --watch", + "test-coverage": "jest --coverage && start coverage/lcov-report/index.html", + "karma-travis": "karma start --single-run --browsers PhantomJS", + "karma-dev": "karma start", + "tslint": "tslint -c tslint.json 'src/**/*.ts'", + "prebuild-clean": "rimraf lib", + "build-demo": "node utils/build-demo.js", + "build-icons": "node utils/create-icon-map.js", + "build-themes": "node-sass --include-path src/style/scss/themes src/style/scss/themes/1802/style.scss css/theme_1802.css", + "build-svg-icons": "node utils/create-svg-icons-map.js", + "build-common": "npm run build-icons && npm run build-svg-icons && node-sass --include-path src/style/scss src/style/scss/style.scss css/style.css && npm run build-themes", + "build-react": "babel src/react -d lib/react", + "compile-angular": "ngc -p tsconfig.angular.build-es5.json", + "rollup-angular:module": "rollup -c rollup.angular.module.config.js", + "rollup-angular:umd": "rollup -c rollup.angular.umd.config.js", + "rollup-angular:metadata": "find build/* -type f -not -name '*.js' -not -name '*.js.*' -exec cp --parents {} lib \\; && cp -rf lib/build/* lib/ && rm -r lib/build", + "build-angular": "npm run compile-angular && npm run rollup-angular:module && npm run rollup-angular:umd && npm run rollup-angular:metadata", + "build-style": "npm run build-style-scss && npm run build-style-less", + "build-style-scss": "cp -rf src/style lib/", + "build-style-less": "sass2less --cwd lib/style/scss **/*.scss ../less/{dir}/{name}.less", + "build-pack": "cp -r css lib/ && cp -r assets lib/", + "postbuild-clean": "rimraf build", + "build": "npm run prebuild-clean && npm run build-common && npm run build-react && npm run build-angular && npm run build-pack && npm run build-style && npm run postbuild-clean", + "storybook": "npm run build-common && start-storybook -p 6006", + "build-storybook": "npm run build-common && build-storybook -c .storybook -o .out/react && ncp utils/index-for-gh-pages.html .out/index.html && ncp utils/main-page.html .out/main-page.html && ncp assets .out/assets", + "lab": "npm run build-common && ng2-component-lab --config .ng2-component-lab/ng2-component-lab.config.js -- feature", + "build-lab": "set NODE_ENV=build&& ng2-component-lab --config .ng2-component-lab/ng2-component-lab.config.js --build -- feature", + "build-gh-pages": "npm run build-storybook && npm run build-lab && ncp utils/index-for-gh-pages.html .out/index.html && ncp utils/main-page.html .out/main-page.html && ncp assets .out/assets" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/onap-sdc/sdc-ui.git" + }, + "author": "", + "bugs": { + "url": "https://github.com/onap-sdc/sdc-ui/issues" + }, + "files": [ + "lib", + "css", + "assets", + "README.md" + ], + "homepage": "https://github.com/onap-sdc/sdc-ui#readme", + "peerDependencies": { + "@angular/core": "^2.4.8" + }, + "devDependencies": { + "@angular/compiler": "~2.4.8", + "@angular/compiler-cli": "~2.4.8", + "@islavi/ng2-component-lab": "^1.0.28", + "@types/jasmine": "2.5.47", + "@types/node": "^7.0.27", + "angular2-router-loader": "^0.3.4", + "angular2-template-loader": "^0.6.2", + "awesome-typescript-loader": "^3.1.3", + "babel-cli": "^6.24.1", + "babel-eslint": "^7.2.3", + "babel-plugin-transform-object-rest-spread": "^6.23.0", + "babel-preset-env": "^1.4.0", + "babel-preset-react": "^6.24.1", + "babel-runtime": "^6.23.0", + "chalk": "1.1.3", + "codelyzer": "3.1.1", + "css-loader": "^0.28.0", + "enzyme": "^2.8.2", + "eslint": "^3.19.0", + "eslint-plugin-import": "^2.2.0", + "eslint-plugin-react": "^7.0.1", + "html-loader": "^0.4.5", + "html-replace-webpack-plugin": "^2.2.6", + "jasmine-core": "~2.5.2", + "jasmine-spec-reporter": "^4.1.1", + "jest": "^20.0.0", + "karma": "~1.5.0", + "karma-chrome-launcher": "^2.2.0", + "karma-coverage": "~1.1.1", + "karma-firefox-launcher": "^1.0.1", + "karma-htmlfile-reporter": "^0.3.5", + "karma-jasmine": "~1.1.0", + "karma-jasmine-html-reporter": "^0.2.2", + "karma-junit-reporter": "^1.2.0", + "karma-phantomjs-launcher": "~1.0.4", + "karma-remap-coverage": "0.1.4", + "karma-sourcemap-loader": "^0.3.7", + "karma-spec-reporter": "0.0.30", + "karma-typescript": "^3.0.8", + "karma-webpack": "2.0.3", + "less-plugin-sass2less": "^1.2.0", + "lite-server": "^2.3.0", + "ncp": "^2.0.0", + "node-sass": "^4.5.3", + "prismjs": "^1.6.0", + "protractor": "5.1.1", + "react-test-renderer": "^15.5.4", + "rimraf": "^2.6.2", + "rollup": "^0.51.8", + "sass-loader": "^6.0.3", + "sorcery": "0.10.0", + "source-map-loader": "0.2.1", + "style-loader": "^0.16.1", + "tslint": "5.9.1", + "tslint-angular": "^1.1.1", + "typescript": "^2.6.1", + "uglify-js": "2.8.29", + "url-parse": "^1.1.9" + }, + "dependencies": { + "@angular/common": "~2.4.8", + "@angular/core": "~2.4.8", + "@angular/forms": "~2.4.8", + "@angular/http": "^2.4.8", + "@angular/platform-browser": "~2.4.8", + "@angular/platform-browser-dynamic": "~2.4.8", + "@angular/router": "~3.2.1", + "@angular/upgrade": "^2.4.8", + "@storybook/react": "^3.1.5", + "http-loader": "0.0.1", + "prop-types": "^15.6.0", + "react": "15.6.2", + "react-dom": "15.6.2", + "reflect-metadata": "^0.1.3", + "rxjs": "5.4.2", + "svg-react-loader": "^0.4.4", + "zone.js": "^0.8.18" + } +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..d3f133f --- /dev/null +++ b/pom.xml @@ -0,0 +1,162 @@ + + + 4.0.0 + + org.onap.sdc + onap-ui + 1.0.0-SNAPSHOT + pom + onap-ui + + + https://nexus.onap.org + /content/sites/site/org/openecomp/sdc/${project.version} + 176c31dfe190a + yyyyMMdd'T'HHmmss'Z' + + 1.8 + 1.8 + + + + + + + maven-clean-plugin + 2.6.1 + + + clean.dist.folder + clean + + clean + + + + + ${basedir}/node_modules + + + ${basedir}/dist + + + + + + + + + com.github.eirslett + frontend-maven-plugin + 1.6 + + + ${project.parent.basedir} + + + + + install node and npm + + install-node-and-npm + + + v8.11.1 + 5.6.0 + + + + + npm set progress off + + npm + + + set progress=false + + + + + npm install + + npm + + + install + + + + + npm run build + + npm + + + run build + + + + + npm test + + npm + + + run test + + + + + npm run build-gh-pages + + npm + + + run build-gh-pages + + + + + + + + + + + + ecomp-releases + Release Repository + ${nexus.proxy}/content/repositories/releases/ + + + ecomp-snapshots + Snapshots Repository + ${nexus.proxy}/content/repositories/snapshots/ + + + ecomp-public + Public Repository + ${nexus.proxy}/content/repositories/public/ + + + + + + ecomp-releases + Release Repository + ${nexus.proxy}/content/repositories/releases/ + + + ecomp-snapshots + Snapshot Repository + ${nexus.proxy}/content/repositories/snapshots/ + + + ecomp-site + dav:${nexus.proxy}${sitePath} + + + + + diff --git a/rollup.angular.module.config.js b/rollup.angular.module.config.js new file mode 100644 index 0000000..18f382b --- /dev/null +++ b/rollup.angular.module.config.js @@ -0,0 +1,33 @@ +export default { + input: './build/angular/index.js', + output: { + name: 'sdcUiAngular', + file: './lib/angular/index.js', + format: 'es', + exports: 'named', + }, + external: [ + '@angular/core', + '@angular/common', + '@angular/common/http', + '@angular/upgrade/static', + '@angular/forms', + '@angular/platform-browser', + '@angular/http', + '@rxjs/add/operator/debounceTime', + 'rxjs/Subject', + 'rxjs/add/operator/debounceTime', + 'rxjs/add/operator/map', + 'rxjs/BehaviorSubject', + 'rxjs/Subject' + ], + onwarn: function(warning) { + // Skip certain warnings + + // should intercept ... but doesn't in some rollup versions + if ( warning.code === 'THIS_IS_UNDEFINED' ) { return; } + + // console.warn everything else + console.warn( warning.message ); + } +}; diff --git a/rollup.angular.umd.config.js b/rollup.angular.umd.config.js new file mode 100644 index 0000000..547c6a9 --- /dev/null +++ b/rollup.angular.umd.config.js @@ -0,0 +1,38 @@ +export default { + input: './build/angular/index.js', + output: { + name: 'sdcUiAngular', + file: './lib/angular/index.umd.js', + format: 'umd', + exports: 'named', + globals: { + '@angular/core': 'ngCore', + '@angular/common': 'ngCommon', + '@angular/forms': 'ngForms' + } + }, + external: [ + '@angular/core', + '@angular/common', + '@angular/common/http', + '@angular/upgrade/static', + '@angular/forms', + '@angular/platform-browser', + '@angular/http', + '@rxjs/add/operator/debounceTime', + 'rxjs/Subject', + 'rxjs/add/operator/debounceTime', + 'rxjs/add/operator/map', + 'rxjs/BehaviorSubject', + 'rxjs/Subject' + ], + onwarn: function(warning) { + // Skip certain warnings + + // should intercept ... but doesn't in some rollup versions + if ( warning.code === 'THIS_IS_UNDEFINED' ) { return; } + + // console.warn everything else + console.warn( warning.message ); + } +}; diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000..83d47bf --- /dev/null +++ b/src/README.md @@ -0,0 +1,9 @@ + +# Folder Structure + +### Angular 2 Framework +Contains **Angular 2** based components + + +### React Framework +Contains **React.js** based components diff --git a/src/angular/accordion/accordion.component.html.ts b/src/angular/accordion/accordion.component.html.ts new file mode 100644 index 0000000..ac5f81f --- /dev/null +++ b/src/angular/accordion/accordion.component.html.ts @@ -0,0 +1,21 @@ +export default ` +
    +
    +
    + + + + + + +
    +
    + {{title}} +
    +
    +
    + +
    +
    `; diff --git a/src/angular/accordion/accordion.component.ts b/src/angular/accordion/accordion.component.ts new file mode 100644 index 0000000..b16df89 --- /dev/null +++ b/src/angular/accordion/accordion.component.ts @@ -0,0 +1,27 @@ +/** + * Created by M.S.BIT on 26/04/2018. + */ + +import {Component, Input, Output, EventEmitter} from "@angular/core"; +import {Placement} from "../common/enums"; +import template from './accordion.component.html'; + +@Component({ + selector: 'sdc-accordion', + template: template, +}) +export class AccordionComponent { + + @Input('arrow-direction') arrowDirection: Placement; + @Input('css-class') customCSSClass: string; + @Input('title') title: string; + @Input('open') open: boolean; + @Output('accordionChanged') changed = new EventEmitter(); + + public accordionArrowDirection = Placement; + + public toggleAccordion(){ + this.open = !this.open; + this.changed.emit(this.open); + } +} diff --git a/src/angular/accordion/accordion.module.ts b/src/angular/accordion/accordion.module.ts new file mode 100644 index 0000000..6cda646 --- /dev/null +++ b/src/angular/accordion/accordion.module.ts @@ -0,0 +1,23 @@ +/** + * Created by M.S.BIT on 26/04/2018. + */ +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import {AccordionComponent} from "./accordion.component"; +import {SvgIconModule} from "../svg-icon/svg-icon.module"; + +@NgModule({ + declarations: [ + AccordionComponent + ], + imports: [ + CommonModule, + SvgIconModule + ], + exports: [ + AccordionComponent + ], +}) +export class AccordionModule { + +} diff --git a/src/angular/animations/animation-directives.module.ts b/src/angular/animations/animation-directives.module.ts new file mode 100644 index 0000000..c6a8203 --- /dev/null +++ b/src/angular/animations/animation-directives.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from "@angular/core"; +import { RippleClickAnimationDirective } from "./ripple-click.animation.directive"; +import { CommonModule } from "@angular/common"; + +@NgModule({ + declarations: [ + RippleClickAnimationDirective + ], + imports: [ + CommonModule + ], + exports: [ + RippleClickAnimationDirective + + ], +}) +export class AnimationDirectivesModule { + +} diff --git a/src/angular/animations/ripple-click.animation.directive.ts b/src/angular/animations/ripple-click.animation.directive.ts new file mode 100644 index 0000000..7b9ed55 --- /dev/null +++ b/src/angular/animations/ripple-click.animation.directive.ts @@ -0,0 +1,47 @@ +import { Directive, Input, HostBinding, HostListener } from "@angular/core"; + +export enum RippleAnimationAction { + CLICK = 0, + MOUSE_ENTER = 1 +}; + +@Directive({ + selector: `[SdcRippleClickAnimation]` +}) +export class RippleClickAnimationDirective { + private animated: boolean; + + @Input() rippleClickDisabled: boolean; + @Input() rippleOnAction:RippleAnimationAction = RippleAnimationAction.CLICK; + + @HostBinding('class.sdc-ripple-click__animated') animationClass: string; + + @HostListener('click') onClick() { + if(this.rippleOnAction === RippleAnimationAction.CLICK){ + this.animateStart(); + } + } + + @HostListener('mouseenter') onMouseEnter() { + //console.log("Mouseenter!", this.rippleOnAction); + if(this.rippleOnAction === RippleAnimationAction.MOUSE_ENTER){ + this.animateStart(); + } + } + + private animateStart():void{ + if (!this.rippleClickDisabled) { + this.animated = true; + this.animationClass = 'sdc-ripple-click__animated'; + } + } + @HostListener('animationend') onAnimationComplete() { + this.animated = false; + this.animationClass = ''; + } + + constructor() { + this.rippleClickDisabled = false; + this.animated = false; + } +} diff --git a/src/angular/autocomplete/autocomplete.component.html.ts b/src/angular/autocomplete/autocomplete.component.html.ts new file mode 100644 index 0000000..5df7352 --- /dev/null +++ b/src/angular/autocomplete/autocomplete.component.html.ts @@ -0,0 +1,14 @@ +export default ` +
    + + +
      +
    • {{item.value}}
    • +
    +
    +`; diff --git a/src/angular/autocomplete/autocomplete.component.ts b/src/angular/autocomplete/autocomplete.component.ts new file mode 100644 index 0000000..5570eff --- /dev/null +++ b/src/angular/autocomplete/autocomplete.component.ts @@ -0,0 +1,114 @@ +import { OnInit, animate, Component, EventEmitter, Input, Output, state, style, transition, trigger } from '@angular/core'; +import { FilterBarComponent } from "../filterbar/filter-bar.component"; +import { URLSearchParams, Http } from "@angular/http"; +import { AutocompletePipe } from "./autocomplete.pipe"; +import template from "./autocomplete.component.html"; +import 'rxjs/add/operator/map'; + +export interface IDataSchema { + key: string; + value: string; +} + +@Component({ + selector: 'sdc-autocomplete', + template: template, + animations: [ + trigger('displayResultsAnimation', [ + state('true', style({ + height: '*', + opacity: 1 + })), + state('false', style({ + height: 0, + opacity: 0 + })), + transition('* => *', animate('200ms')) + ]), + ], + providers: [AutocompletePipe] +}) +export class SearchWithAutoCompleteComponent implements OnInit { + @Input() public data: any[] = []; + @Input() public dataSchema: IDataSchema; + @Input() public dataUrl: string; + @Input() public label: string; + @Input() public placeholder: string; + @Output() public itemSelected: EventEmitter = new EventEmitter(); + + private searchQuery: string; + private complexData: any[] = []; + private autoCompleteResults: any[] = []; + private isItemSelected: boolean = false; + + public constructor(private http: Http, private autocompletePipe: AutocompletePipe) { + } + + public ngOnInit(): void { + if (this.data) { + this.handleLocalData(); + } + this.searchQuery = ""; + } + + private handleLocalData = (): void => { + // Convert the data (simple | complex) to unified complex data with key value. + // In case user supplied dataSchema, this is complex data + if (!this.dataSchema) { + this.convertSimpleData(); + } else { + this.convertComplexData(); + } + } + + private convertSimpleData = (): void => { + this.complexData = []; + this.data.forEach((item: any) => { + this.complexData.push({key: item, value: item}); + }); + } + + private convertComplexData = (): void => { + this.complexData = []; + this.data.forEach((item: any) => { + this.complexData.push({key: item[this.dataSchema.key], value: item[this.dataSchema.value]}); + }); + } + + private onItemSelected = (selectedItem: IDataSchema): void => { + this.searchQuery = selectedItem.value; + this.isItemSelected = true; + this.autoCompleteResults = []; + this.itemSelected.emit(selectedItem.key); + } + + private onSearchQueryChanged = (searchText: string): void => { + if (searchText !== this.searchQuery) { + this.searchQuery = searchText; + if (!this.searchQuery) { + this.onClearSearch(); + } else { + if (this.dataUrl) { + const params: URLSearchParams = new URLSearchParams(); + params.set('searchQuery', this.searchQuery); + this.http.get(this.dataUrl, {search: params}) + .map((response) => { + this.data = response.json(); + this.handleLocalData(); + this.autoCompleteResults = this.complexData; + }).subscribe(); + } else { + this.autoCompleteResults = this.autocompletePipe.transform(this.complexData, this.searchQuery); + } + } + this.isItemSelected = false; + } + } + + private onClearSearch = (): void => { + this.autoCompleteResults = []; + if (this.isItemSelected) { + this.itemSelected.emit(); + } + } +} diff --git a/src/angular/autocomplete/autocomplete.module.ts b/src/angular/autocomplete/autocomplete.module.ts new file mode 100644 index 0000000..1bead47 --- /dev/null +++ b/src/angular/autocomplete/autocomplete.module.ts @@ -0,0 +1,23 @@ +import { NgModule } from "@angular/core"; +import { SearchWithAutoCompleteComponent } from "./autocomplete.component"; +import { CommonModule } from "@angular/common"; +import { FilterBarModule } from "../filterbar/filter-bar.module"; +import { AutocompletePipe } from "./autocomplete.pipe"; +import { HttpModule } from '@angular/http'; + +@NgModule({ + declarations: [ + SearchWithAutoCompleteComponent, + AutocompletePipe + ], + imports: [ + FilterBarModule, + CommonModule, + HttpModule + ], + exports: [ + SearchWithAutoCompleteComponent + ], +}) +export class AutoCompleteModule { +} diff --git a/src/angular/autocomplete/autocomplete.pipe.ts b/src/angular/autocomplete/autocomplete.pipe.ts new file mode 100644 index 0000000..bee24ab --- /dev/null +++ b/src/angular/autocomplete/autocomplete.pipe.ts @@ -0,0 +1,16 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { IDataSchema } from './autocomplete.component'; + +@Pipe ({ + name: 'AutocompletePipe', +}) +export class AutocompletePipe implements PipeTransform { + public transform(data: IDataSchema[], text: string) { + if (!text || !text.length) { + return data; + } + return data.filter((item: IDataSchema) => { + return item.value.toLowerCase().indexOf(text.toLowerCase()) > -1; + }); + } +} diff --git a/src/angular/buttons/button.component.html.ts b/src/angular/buttons/button.component.html.ts new file mode 100644 index 0000000..f903fd1 --- /dev/null +++ b/src/angular/buttons/button.component.html.ts @@ -0,0 +1,15 @@ +export default ` + + +`; diff --git a/src/angular/buttons/button.component.ts b/src/angular/buttons/button.component.ts new file mode 100644 index 0000000..1f049dc --- /dev/null +++ b/src/angular/buttons/button.component.ts @@ -0,0 +1,62 @@ +import { Component, HostBinding, Input, OnInit } from "@angular/core"; +import { Placement } from "../common/enums"; +import template from "./button.component.html"; + +@Component({ + selector: "sdc-button", + template: template +}) + +export class ButtonComponent implements OnInit { + @Input() public text: string; + @Input() public disabled: boolean; + @Input() public type: string; + @Input() public size: string; + @Input() public preventDoubleClick: boolean; + @Input() public icon_name: string; + @Input() public icon_position: string; + @Input() public show_spinner: boolean; + @Input() public spinner_position: Placement; + @Input() public testId: string; + + public placement = Placement; + private lastClick: Date; + private iconPositionClass: string; + private iconMode: string; + + @HostBinding('class.sdc-button__wrapper') true; + + constructor() { + this.type = "primary"; + this.size = "default"; + this.disabled = false; + this.iconMode = 'primary'; + } + + public ngOnInit(): void { + this.iconPositionClass = this.icon_position ? 'sdc-icon-' + this.icon_position : ''; + this.iconMode = (this.type === "primary") ? 'info' : 'primary'; + } + + public onClick = (e): void => { + const now: Date = new Date(); + if ( this.preventDoubleClick && this.lastClick && (now.getTime() - this.lastClick.getTime()) <= 500 ) { + e.preventDefault(); + e.stopPropagation(); + } + this.lastClick = now; + } + + public disableButton = () => { + if (!this.disabled) { + this.disabled = true; + } + } + + public enableButton = () => { + if (this.disabled) { + this.disabled = false; + } + } + +} diff --git a/src/angular/buttons/buttons.module.ts b/src/angular/buttons/buttons.module.ts new file mode 100644 index 0000000..c804758 --- /dev/null +++ b/src/angular/buttons/buttons.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from "@angular/core"; +import { ButtonComponent } from "./button.component"; +import { CommonModule } from "@angular/common"; +import { SvgIconModule } from './../svg-icon/svg-icon.module'; + +@NgModule({ + declarations: [ + ButtonComponent + ], + imports: [ + CommonModule, + SvgIconModule + ], + exports: [ + ButtonComponent + + ], +}) +export class ButtonsModule { + +} diff --git a/src/angular/checklist/checklist.component.html.ts b/src/angular/checklist/checklist.component.html.ts new file mode 100644 index 0000000..cb6f540 --- /dev/null +++ b/src/angular/checklist/checklist.component.html.ts @@ -0,0 +1,15 @@ +export default ` +
    +
    + +
    +
    + +
    +
    +`; diff --git a/src/angular/checklist/checklist.component.ts b/src/angular/checklist/checklist.component.ts new file mode 100644 index 0000000..386cd3e --- /dev/null +++ b/src/angular/checklist/checklist.component.ts @@ -0,0 +1,50 @@ +import { Component, EventEmitter, Input, Output } from "@angular/core"; +import { ChecklistModel } from "./models/Checklist"; +import { ChecklistItemModel } from "./models/ChecklistItem"; +import template from "./checklist.component.html"; + +@Component({ + selector: 'sdc-checklist', + template: template +}) +export class ChecklistComponent { + @Input() public checklistModel: ChecklistModel; + @Output() public checkedChange: EventEmitter = new EventEmitter(); + + private checkboxCheckedChange(checkbox: ChecklistItemModel, currentChecklistModel: ChecklistModel, stopPropagation?: boolean) { + // push/pop the checkbox value + if (checkbox.isChecked) { + currentChecklistModel.selectedValues.push(checkbox.value); + }else { + const index: number = currentChecklistModel.selectedValues.indexOf(checkbox.value); + currentChecklistModel.selectedValues.splice(index, 1); + } + if (!stopPropagation) { + if (checkbox.subLevelChecklist && + ((checkbox.isChecked && checkbox.subLevelChecklist.selectedValues.length < checkbox.subLevelChecklist.checkboxes.length) || + (!checkbox.isChecked && checkbox.subLevelChecklist.selectedValues.length))) { + checkbox.subLevelChecklist.checkboxes.forEach((childCheckbox: ChecklistItemModel) => { + if (childCheckbox.isChecked !== checkbox.isChecked) { + childCheckbox.isChecked = checkbox.isChecked; + this.checkboxCheckedChange(childCheckbox, checkbox.subLevelChecklist); + } + }); + } + } + // raise event + this.checkedChange.emit(checkbox); + } + + private childCheckboxChange(updatedCheckbox: ChecklistItemModel, parentCheckbox: ChecklistItemModel) { + let updatedValues: any[] = parentCheckbox.subLevelChecklist.selectedValues; + if (parentCheckbox.isChecked !== (updatedValues.length === parentCheckbox.subLevelChecklist.checkboxes.length)) { + parentCheckbox.isChecked = updatedValues.length === parentCheckbox.subLevelChecklist.checkboxes.length; + this.checkboxCheckedChange(parentCheckbox, this.checklistModel, true); + } + this.checkedChange.emit(updatedCheckbox); + } + + private hasCheckedChild(currentCheckbox: Element): boolean { + return !!currentCheckbox.querySelector(".sdc-checkbox__input:checked"); + } +} diff --git a/src/angular/checklist/checklist.module.ts b/src/angular/checklist/checklist.module.ts new file mode 100644 index 0000000..013bf9b --- /dev/null +++ b/src/angular/checklist/checklist.module.ts @@ -0,0 +1,11 @@ +import { NgModule } from "@angular/core"; +import { ChecklistComponent } from "./checklist.component"; +import { CommonModule } from "@angular/common"; +import { FormElementsModule } from "../form-elements/form-elements.module"; + +@NgModule({ + declarations: [ChecklistComponent], + exports: [ChecklistComponent], + imports: [CommonModule, FormElementsModule] +}) +export class ChecklistModule {} diff --git a/src/angular/checklist/models/Checklist.ts b/src/angular/checklist/models/Checklist.ts new file mode 100644 index 0000000..7b50dd3 --- /dev/null +++ b/src/angular/checklist/models/Checklist.ts @@ -0,0 +1,18 @@ +import { ChecklistItemModel } from "./ChecklistItem"; + +export class ChecklistModel { + public selectedValues: any[]; + public checkboxes: ChecklistItemModel[]; + constructor(selectedValues: any[], checkboxes: ChecklistItemModel[]) { + this.selectedValues = selectedValues || []; + this.checkboxes = checkboxes; + // align the selected values list and checkboxes isChecked param + this.checkboxes.forEach((checkbox: ChecklistItemModel) => { + if (this.selectedValues.indexOf(checkbox.value) > -1) { + checkbox.isChecked = true; + }else if (checkbox.isChecked) { + this.selectedValues.push(checkbox.value); + } + }); + } +} diff --git a/src/angular/checklist/models/ChecklistItem.ts b/src/angular/checklist/models/ChecklistItem.ts new file mode 100644 index 0000000..e2d812a --- /dev/null +++ b/src/angular/checklist/models/ChecklistItem.ts @@ -0,0 +1,17 @@ +import { ChecklistModel } from "./Checklist"; +import { isUndefined } from "util"; + +export class ChecklistItemModel { + public label: string; + public value: any; + public disabled: boolean; + public isChecked: boolean; + public subLevelChecklist: ChecklistModel; + constructor(label: string, disabled?: boolean, isChecked?: boolean, subLevelChecklist?: ChecklistModel, value?: any) { + this.label = label; + this.disabled = disabled; + this.isChecked = isChecked; + this.value = isUndefined(value) ? label : value; + this.subLevelChecklist = subLevelChecklist; + } +} diff --git a/src/angular/common/enums.ts b/src/angular/common/enums.ts new file mode 100644 index 0000000..0825d2f --- /dev/null +++ b/src/angular/common/enums.ts @@ -0,0 +1,34 @@ +/* +This file includes all common enum types. + +NOTE: The string values might be used as css class names. +*/ + +export enum Size { + x_large = 'x_large', + large = 'large', + medium = 'medium', + small = 'small', + x_small = 'x_small' +} + +export enum Mode { + primary = 'primary', + secondary = 'secondary', + success = 'success', + error = 'error', + warning = 'warning', + info = 'info' +} + +export enum Placement { + left = 'left', + right = 'right', + top = 'top', + bottom = 'bottom' +} + +export enum RegexPatterns { + email = '^(([^<>()\\[\\]\\\\.,;:\\s@"]+(\.[^<>()\\[\\]\\\\.,;:\\s@"]+)*)|(".+"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$', + numbers = '^\\d+$' +} diff --git a/src/angular/common/index.ts b/src/angular/common/index.ts new file mode 100644 index 0000000..839eba9 --- /dev/null +++ b/src/angular/common/index.ts @@ -0,0 +1,3 @@ +import * as SdcUiCommon from './enums'; + +export { SdcUiCommon }; diff --git a/src/angular/components.ts b/src/angular/components.ts new file mode 100644 index 0000000..8e06197 --- /dev/null +++ b/src/angular/components.ts @@ -0,0 +1,50 @@ +/* + Exports all the components and services of sdc-ui. + */ + +// Form Elements +export { InputComponent } from "./form-elements/input/input.component"; +export { DropDownComponent } from "./form-elements/dropdown/dropdown.component"; +export { CheckboxComponent } from "./form-elements/checkbox/checkbox.component"; +export { RadioGroupComponent } from "./form-elements/radios/radio-buttons-group.component"; + +// Buttons +export { ButtonComponent } from "./buttons/button.component"; + +// Modals +export { ModalComponent } from "./modals/modal.component"; +export { ModalService } from "./modals/modal.service"; +export { ModalButtonComponent } from "./modals/modal-button.component"; + +// Notifications +export { NotificationComponent } from "./notifications/notification/notification.component"; +export { NotificationContainerComponent } from "./notifications/container/notifcontainer.component"; +export { NotificationsService } from "./notifications/services/notifications.service"; + +// Popup Menu +export { PopupMenuListComponent } from "./popup-menu/popup-menu-list.component"; +export { PopupMenuItemComponent } from "./popup-menu/popup-menu-item.component"; + +// Tiles +export { TileComponent } from "./tiles/tile.component"; +export { TileContentComponent } from "./tiles/children/tile-content.component"; +export { TileFooterComponent } from "./tiles/children/tile-footer.component"; +export { TileHeaderComponent } from "./tiles/children/tile-header.component"; + +// Check List +export { ChecklistComponent } from "./checklist/checklist.component"; + +// Tag Cloud +export { TagItemComponent } from "./tag-cloud/tag-item/tag-item.component"; +export { TagCloudComponent } from "./tag-cloud/tag-cloud.component"; + +// Tabs +export { TabsComponent } from "./tabs/tabs.component"; +export { TabComponent } from './tabs/children/tab.component'; + +// Svg Icons +export { SvgIconComponent } from "./svg-icon/svg-icon.component"; +export { SvgIconLabelComponent } from "./svg-icon/svg-icon-label.component"; + +// Accordion +export { AccordionComponent } from './accordion/accordion.component'; diff --git a/src/angular/filterbar/filter-bar.component.html.ts b/src/angular/filterbar/filter-bar.component.html.ts new file mode 100644 index 0000000..a7d55e2 --- /dev/null +++ b/src/angular/filterbar/filter-bar.component.html.ts @@ -0,0 +1,30 @@ +export default ` +
    + + + + + + + + + + + + + + + + + + + + + +
    +`; diff --git a/src/angular/filterbar/filter-bar.component.ts b/src/angular/filterbar/filter-bar.component.ts new file mode 100644 index 0000000..49cc154 --- /dev/null +++ b/src/angular/filterbar/filter-bar.component.ts @@ -0,0 +1,30 @@ +import { Component, Input, Output, EventEmitter, HostBinding } from '@angular/core'; +import template from "./filter-bar.component.html"; + +@Component({ + selector: 'sdc-filter-bar', + template: template +}) +export class FilterBarComponent { + + @HostBinding('class') classes = 'sdc-filter-bar'; + + @Input() public placeholder: string; + @Input() public label: string; + @Input() public debounceTime: number; + + @Input() public searchQuery: string; + @Output() public searchQueryChange: EventEmitter = new EventEmitter(); + + constructor() { + this.debounceTime = 200; + } + + private searchTextChange = ($event): void => { + this.searchQueryChange.emit($event); + } + + private clearSearchQuery = (): void => { + this.searchQuery = ""; + } +} diff --git a/src/angular/filterbar/filter-bar.module.ts b/src/angular/filterbar/filter-bar.module.ts new file mode 100644 index 0000000..c3604ed --- /dev/null +++ b/src/angular/filterbar/filter-bar.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from "@angular/core"; +import { FilterBarComponent } from "./filter-bar.component"; +import { CommonModule } from "@angular/common"; +import { FormElementsModule } from "../form-elements/form-elements.module"; + +@NgModule({ + declarations: [ + FilterBarComponent + ], + imports: [CommonModule, + FormElementsModule], + exports: [ + FilterBarComponent + ], +}) +export class FilterBarModule { +} diff --git a/src/angular/form-elements/checkbox/checkbox.component.html.ts b/src/angular/form-elements/checkbox/checkbox.component.html.ts new file mode 100644 index 0000000..f4031db --- /dev/null +++ b/src/angular/form-elements/checkbox/checkbox.component.html.ts @@ -0,0 +1,8 @@ +export default ` +
    + +
    +`; diff --git a/src/angular/form-elements/checkbox/checkbox.component.spec.ts b/src/angular/form-elements/checkbox/checkbox.component.spec.ts new file mode 100644 index 0000000..36f478e --- /dev/null +++ b/src/angular/form-elements/checkbox/checkbox.component.spec.ts @@ -0,0 +1,37 @@ +import { TestBed, async } from '@angular/core/testing'; +import { CheckboxComponent } from "./checkbox.component"; +import { AnimationDirectivesModule } from "../../animations/animation-directives.module"; +import { FormsModule } from "@angular/forms"; + + +describe("Checbox Tests", ()=>{ + let component: CheckboxComponent; + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + CheckboxComponent + ], + imports:[ + FormsModule, + AnimationDirectivesModule + ] + }).compileComponents(); + const fixture = TestBed.createComponent(CheckboxComponent); + component = fixture.componentInstance; + })); + + it("Component Created", async(()=> { + expect(component).toBeDefined(); + })); + + it( "Test Value suppose to be toggled", async( ()=> { + component.toggleState(true) + expect(component.checked).toEqual(true); + })); + + it( "If disabled not toggled"), async(()=>{ + component.disabled = true; + component.toggleState(true); + expect(component.checked).toEqual(false); + }); +}); diff --git a/src/angular/form-elements/checkbox/checkbox.component.ts b/src/angular/form-elements/checkbox/checkbox.component.ts new file mode 100644 index 0000000..ec05eac --- /dev/null +++ b/src/angular/form-elements/checkbox/checkbox.component.ts @@ -0,0 +1,21 @@ +import { Component, Input, Output, EventEmitter, ViewEncapsulation } from '@angular/core'; +import template from "./checkbox.component.html"; + +@Component({ + selector: 'sdc-checkbox', + template: template, + encapsulation: ViewEncapsulation.None +}) +export class CheckboxComponent { + @Input() label:string; + @Input() checked:boolean; + @Input() disabled:boolean; + @Output() checkedChange:EventEmitter = new EventEmitter(); + + public toggleState(newState:boolean) { + if (!this.disabled) { + this.checked = newState; + this.checkedChange.emit(newState); + } + } +} diff --git a/src/angular/form-elements/dropdown/dropdown-models.ts b/src/angular/form-elements/dropdown/dropdown-models.ts new file mode 100644 index 0000000..fa8dc23 --- /dev/null +++ b/src/angular/form-elements/dropdown/dropdown-models.ts @@ -0,0 +1,18 @@ +export enum DropDownTypes { + Regular, + Headless, + Auto +} + +export enum DropDownOptionType { + Simple, // default + Header, + Disable, + HorizontalLine +} + +export interface IDropDownOption { + value: any; + label: string; + type?: DropDownOptionType; +} diff --git a/src/angular/form-elements/dropdown/dropdown-trigger.directive.ts b/src/angular/form-elements/dropdown/dropdown-trigger.directive.ts new file mode 100644 index 0000000..94ab3bc --- /dev/null +++ b/src/angular/form-elements/dropdown/dropdown-trigger.directive.ts @@ -0,0 +1,17 @@ +import { Directive, Input, HostBinding, HostListener } from "@angular/core"; +import { DropDownComponent } from "./dropdown.component"; + +@Directive({ + selector: '[SdcDropdownTrigger]' +}) + +export class DropDownTriggerDirective { + + @HostBinding('class.js-sdc-dropdown--toggle-hook') true; + @Input() dropDown: DropDownComponent; + + @HostListener('click', ['$event']) onClick = (event) => { + this.dropDown.toggleDropdown(event); + } + +} diff --git a/src/angular/form-elements/dropdown/dropdown.component.html.ts b/src/angular/form-elements/dropdown/dropdown.component.html.ts new file mode 100644 index 0000000..a4247a4 --- /dev/null +++ b/src/angular/form-elements/dropdown/dropdown.component.html.ts @@ -0,0 +1,59 @@ +export default ` +
    + +
    + + +
    + + +
    + + + + + + + +
    +
      + + +
    • {{option.label || String(option.value)}}
    • + +
      +
    +
    + + +
    +
    +`; diff --git a/src/angular/form-elements/dropdown/dropdown.component.spec.ts b/src/angular/form-elements/dropdown/dropdown.component.spec.ts new file mode 100644 index 0000000..1c0cb4d --- /dev/null +++ b/src/angular/form-elements/dropdown/dropdown.component.spec.ts @@ -0,0 +1,71 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { DropDownComponent } from './dropdown.component'; +import { IDropDownOption, DropDownTypes } from "./dropdown-models"; +import { FormsModule } from "@angular/forms"; +import {SvgIconModule} from "../../svg-icon/svg-icon.module"; + + +const label:string = "DropDown example"; +const placeHolder:string = "Please choose option"; +const options:IDropDownOption[] = [ + { + label:'First Option', + value: 'First Option' + }, + { + label:'Second Option', + value: 'Second Option' + }, + { + label:'Third Option', + value: 'Third Option' + } +]; + +describe('DropDown component', () => { + let fixture: ComponentFixture; + let component: DropDownComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DropDownComponent ], + imports:[ + FormsModule, + SvgIconModule + ] + }).compileComponents(); + fixture = TestBed.createComponent(DropDownComponent); + component = fixture.componentInstance; + + })); + + beforeEach(()=>{ + component.label = label; + component.placeHolder = placeHolder; + component.options = options; + component.type = DropDownTypes.Regular; + console.log('herer we got component', component) + fixture.detectChanges(); + }); + + it('component should be created', () => { + expect(component).toBeTruthy(); + }); + + it('component should export the selected value', () => { + const option = options[1]; + component.selectOption(option); + fixture.detectChanges(); + expect(component.selectedOption).toEqual(option); + }); + + it('component should have autocomplite', () => { + expect(component.options.length).toEqual(3); + component.type = DropDownTypes.Auto; + component.filterValue = 'testERrorotesttresadfadfasdfasf'; + fixture.detectChanges(); + component.filterOptions(component.filterValue); + expect(component.options.length).toEqual(0); + }); + +}); diff --git a/src/angular/form-elements/dropdown/dropdown.component.ts b/src/angular/form-elements/dropdown/dropdown.component.ts new file mode 100644 index 0000000..a23072f --- /dev/null +++ b/src/angular/form-elements/dropdown/dropdown.component.ts @@ -0,0 +1,149 @@ +import { Component, EventEmitter, Input, Output, forwardRef, OnChanges, SimpleChanges, OnInit, ElementRef, ViewChild, AfterViewInit, HostListener, Renderer } from '@angular/core'; +import { IDropDownOption, DropDownOptionType, DropDownTypes } from "./dropdown-models"; +import { ValidatableComponent } from './../validation/validatable.component'; +import template from './dropdown.component.html'; + +@Component({ + selector: 'sdc-dropdown', + template: template +}) +export class DropDownComponent extends ValidatableComponent implements OnChanges, OnInit { + + @Output('changed') changeEmitter:EventEmitter = new EventEmitter(); + @Input() label: string; + @Input() options: IDropDownOption[]; + @Input() disabled: boolean; + @Input() placeHolder: string; + @Input() required: boolean; + @Input() maxHeight: number; + @Input() selectedOption: IDropDownOption; + @Input() type: DropDownTypes = DropDownTypes.Regular; + @ViewChild('dropDownWrapper') dropDownWrapper: ElementRef; + @ViewChild('optionsContainerElement') optionsContainerElement: ElementRef; + @HostListener('document:click', ['$event']) onClick(e) { + this.onClickDocument(e); + } + + private bottomVisible = true; + private myRenderer: Renderer; + + // Drop-down show/hide flag. default is false (closed) + public show = false; + + // Export DropDownOptionType enum so we can use it on the template + public cIDropDownOptionType = DropDownOptionType; + public cIDropDownTypes = DropDownTypes; + + // Configure unselectable option types + private unselectableOptions = [ + DropDownOptionType.Disable, + DropDownOptionType.Header, + DropDownOptionType.HorizontalLine + ]; + + // Set or unset Group style on drop-down + public isGroupDesign = false; + public animation_init = false; + public allOptions: IDropDownOption[]; + public filterValue: string; + + constructor(public renderer: Renderer) { + super(); + this.myRenderer = renderer; + this.maxHeight = 244; + this.filterValue = ''; + } + + ngOnInit(): void { + if (this.options) { + this.allOptions = this.options; + if (this.options.find(option => option.type === DropDownOptionType.Header)) { + this.isGroupDesign = true; + } + } + } + + ngOnChanges(changes: SimpleChanges): void { + console.log("ngOnChanges"); + if (changes.selectedOption && changes.selectedOption.currentValue !== changes.selectedOption.previousValue) { + if (typeof changes.selectedOption.currentValue === 'string' && this.isSelectable(changes.selectedOption.currentValue)) { + this.setSelected(changes.selectedOption.currentValue); + } else if (this.isSelectable(changes.selectedOption.currentValue.value)) { + this.setSelected(changes.selectedOption.currentValue.value); + } else { + this.setSelected(undefined); + } + } + } + + public getValue(): any { + return this.selectedOption && this.selectedOption.value; + } + + public selectOption = (option: IDropDownOption | string, event?): void => { + if (event) { event.stopPropagation(); } + if (this.type === DropDownTypes.Headless) { + // Hide the options when in headless mode and user select option. + this.myRenderer.setElementStyle(this.dropDownWrapper.nativeElement, 'display', 'none'); + } + if (typeof option === 'string' && this.isSelectable(option)) { + this.setSelected(option); + } else if (this.isSelectable((option as IDropDownOption).value)) { + this.setSelected((option as IDropDownOption).value); + } + } + + public toggleDropdown = (event?): void => { + if (event) { event.stopPropagation(); } + if (this.type === DropDownTypes.Headless) { + // Show the options when in headless mode. + this.myRenderer.setElementStyle(this.dropDownWrapper.nativeElement, 'display', 'block'); + } + if (this.disabled) { return; } + this.animation_init = true; + this.bottomVisible = this.isBottomVisible(); + this.show = !this.show; + } + + public filterOptions = (filterValue): void => { + if (filterValue.length >= 1 && !this.show) { this.toggleDropdown(); } + if (this.selectedOption) { this.selectedOption = null; } + this.options = this.allOptions.filter((option) => { + return option.value.toLowerCase().indexOf(filterValue.toLowerCase()) > -1; + }); + } + + private isSelectable = (value: string): boolean => { + const option: IDropDownOption = this.options.find(o => o.value === value); + if (!option) { return false; } + if (!option.type) { return true; } + return !this.unselectableOptions.find(optionType => optionType === option.type); + } + + private setSelected = (value: string): void => { + this.selectedOption = this.options.find(o => o.value === value); + if (this.type === DropDownTypes.Auto) { this.filterValue = value; } + this.show = false; + this.changeEmitter.next(this.selectedOption); + } + + private isBottomVisible = (): boolean => { + const windowPos = window.innerHeight + window.pageYOffset; + const boundingRect = this.dropDownWrapper.nativeElement.getBoundingClientRect(); + const dropDownPos = boundingRect.top + boundingRect.height + this.maxHeight; + return windowPos > dropDownPos; + } + + private onClickDocument = (event): void => { + if (this.type === DropDownTypes.Headless) { + if (!this.optionsContainerElement.nativeElement.contains(event.target)) { + this.show = false; + } + } else { + if (!this.dropDownWrapper.nativeElement.contains(event.target)) { + this.show = false; + } + } + } + +} diff --git a/src/angular/form-elements/form-elements.module.ts b/src/angular/form-elements/form-elements.module.ts new file mode 100644 index 0000000..744f8b8 --- /dev/null +++ b/src/angular/form-elements/form-elements.module.ts @@ -0,0 +1,38 @@ +import { NgModule } from "@angular/core"; +import { FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { InputComponent } from "./input/input.component"; +import { DropDownComponent } from "./dropdown/dropdown.component"; +import { CommonModule } from "@angular/common"; +import { CheckboxComponent } from "./checkbox/checkbox.component"; +import { RadioGroupComponent } from "./radios/radio-buttons-group.component"; +import { AnimationDirectivesModule } from '../animations/animation-directives.module'; +import { DropDownTriggerDirective } from "./dropdown/dropdown-trigger.directive"; +import {SvgIconModule} from "../svg-icon/svg-icon.module"; +import { ValidationModule } from './validation/validation.module'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + AnimationDirectivesModule, + SvgIconModule + ], + declarations: [ + DropDownComponent, + InputComponent, + CheckboxComponent, + RadioGroupComponent, + DropDownTriggerDirective, + ], + exports: [ + DropDownComponent, + DropDownTriggerDirective, + InputComponent, + CheckboxComponent, + RadioGroupComponent, + ValidationModule + ] +}) +export class FormElementsModule { +} diff --git a/src/angular/form-elements/input/input.component.html.ts b/src/angular/form-elements/input/input.component.html.ts new file mode 100644 index 0000000..f8a4609 --- /dev/null +++ b/src/angular/form-elements/input/input.component.html.ts @@ -0,0 +1,19 @@ +export default ` +
    + + +
    +`; diff --git a/src/angular/form-elements/input/input.component.ts b/src/angular/form-elements/input/input.component.ts new file mode 100644 index 0000000..af0e9f4 --- /dev/null +++ b/src/angular/form-elements/input/input.component.ts @@ -0,0 +1,54 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { FormControl } from "@angular/forms"; +import { ValidationComponent } from '../validation/validation.component'; +import { ValidatableComponent } from './../validation/validatable.component'; +import 'rxjs/add/operator/debounceTime'; +import template from "./input.component.html"; + +@Component({ + selector: 'sdc-input', + template: template, +}) +export class InputComponent extends ValidatableComponent implements OnInit { + + @Output('valueChange') public baseEmitter: EventEmitter = new EventEmitter(); + @Input() public label: string; + @Input() public value: any; + @Input() public name: string; + @Input() public classNames: string; + @Input() public disabled: boolean; + @Input() public type: string; + @Input() public placeHolder: string; + @Input() public required: boolean; + @Input() public minLength: number; + @Input() public maxLength: number; + @Input() public debounceTime: number; + @Input() public testId: string; + + protected control: FormControl; + + constructor() { + super(); + this.control = new FormControl('', []); + this.debounceTime = 0; + this.placeHolder = ''; + this.type = 'text'; + } + + ngOnInit() { + this.control.valueChanges. + debounceTime(this.debounceTime) + .subscribe((newValue: any) => { + this.baseEmitter.emit(this.value); + }); + } + + public getValue(): any { + return this.value; + } + + onKeyPress(value: string) { + this.valueChanged(this.value); + } + +} diff --git a/src/angular/form-elements/radios/radio-button.model.ts b/src/angular/form-elements/radios/radio-button.model.ts new file mode 100644 index 0000000..1ad4b3f --- /dev/null +++ b/src/angular/form-elements/radios/radio-button.model.ts @@ -0,0 +1,15 @@ +export interface IRadioButtonModel { + label: string; + disabled: boolean; + name: string; + value: string; +}; + +export interface IOptionGroup { + items: IRadioButtonModel[]; +}; + +export enum Direction { + vertical, + horizontal +} diff --git a/src/angular/form-elements/radios/radio-buttons-group.component.html.ts b/src/angular/form-elements/radios/radio-buttons-group.component.html.ts new file mode 100644 index 0000000..28a27af --- /dev/null +++ b/src/angular/form-elements/radios/radio-buttons-group.component.html.ts @@ -0,0 +1,20 @@ +export default ` + +
    + +
    +`; diff --git a/src/angular/form-elements/radios/radio-buttons-group.component.spec.ts b/src/angular/form-elements/radios/radio-buttons-group.component.spec.ts new file mode 100644 index 0000000..273a701 --- /dev/null +++ b/src/angular/form-elements/radios/radio-buttons-group.component.spec.ts @@ -0,0 +1,52 @@ +import { TestBed, async } from '@angular/core/testing'; +import { RadioGroupComponent } from "./radio-buttons-group.component"; +import { FormsModule } from "@angular/forms"; +import { IRadioButtonModel } from "./radio-button.model"; +import { AnimationDirectivesModule } from "../../animations/animation-directives.module"; + + +describe("Radio Buttons unit-tests", ()=>{ + let component: RadioGroupComponent; + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + RadioGroupComponent + ], + imports:[ + FormsModule, + AnimationDirectivesModule + ] + }).compileComponents(); + + const fixture = TestBed.createComponent(RadioGroupComponent); + component = fixture.componentInstance; + component.disabled = false;//TODO constructor + component.options = { + items: [] + }; + })); + + it('Component Created', async(()=> { + expect(component).toBeDefined(); + })); + + it('Not possible to choose value which not exists', async(() =>{ + component.value = 'test'; + expect(component.value).not.toEqual('test'); + })); + + it('Normal flow', async(() =>{ + component.options.items = [ { + value: 'val1', + name: 'exp6', + label: 'Label of Radio1' + }, { + value: 'val2', + name: 'exp6', + label: 'Label of Radio2' + }]; + component.value = component.options.items[0].value; + expect(component.value).toEqual(component.options.items[0].value); + })); + +}); diff --git a/src/angular/form-elements/radios/radio-buttons-group.component.ts b/src/angular/form-elements/radios/radio-buttons-group.component.ts new file mode 100644 index 0000000..800d8b0 --- /dev/null +++ b/src/angular/form-elements/radios/radio-buttons-group.component.ts @@ -0,0 +1,52 @@ +import { Component, Input, Output, ViewEncapsulation, EventEmitter, HostBinding } from "@angular/core"; +import { Direction, IOptionGroup, IRadioButtonModel } from "./radio-button.model"; +import template from './radio-buttons-group.component.html'; + +@Component({ + selector: 'sdc-radio-group', + template: template, + encapsulation: ViewEncapsulation.None +}) +export class RadioGroupComponent { + + private _direction: Direction = Direction.vertical; + private _selectedValue: string; + + @HostBinding('class') classes = 'sdc-radio-group'; + + @Input() public legend: string; + @Input() public options: IOptionGroup; + @Input() public disabled: boolean; + + @Input() + get value(): string { + return this._selectedValue; + } + set value(value: string) { + if (this.isOptionExists(value)) { + this._selectedValue = value; + } + } + + @Output() public valueChange: EventEmitter = new EventEmitter(); + + @Input() + get direction(): string { + return Direction[this._direction]; + } + set direction(direction: string) { + this._direction = (direction === 'horizontal' ? Direction.horizontal : Direction.vertical); + } + + public onValueChanged(value): void { + this.valueChange.emit(value); + } + + private isOptionExists(value) { + const exist = this.options.items.find((item: IRadioButtonModel) => { + return item.value === value; + }); + return exist !== undefined; + } + +} diff --git a/src/angular/form-elements/validation/validatable.component.ts b/src/angular/form-elements/validation/validatable.component.ts new file mode 100644 index 0000000..4817dea --- /dev/null +++ b/src/angular/form-elements/validation/validatable.component.ts @@ -0,0 +1,25 @@ +import { Input, Component } from "@angular/core"; +import { ValidationComponent } from './validation.component'; +import { Subject } from 'rxjs/Subject'; +import { IValidatableComponent } from './validatable.interface'; + +export abstract class ValidatableComponent implements IValidatableComponent { + + // Each ValidatableComponent should handle the style in case of error, according to this boolean + public valid = true; + + // Each ValidatableComponent will notify when the value is changed. + public notifier: Subject; + + constructor() { + this.notifier = new Subject(); + } + + public abstract getValue(): any; + + // Each ValidatableComponent should call the valueChanged on value changed function. + protected valueChanged = (value: any): void => { + this.notifier.next(value); + } + +} diff --git a/src/angular/form-elements/validation/validatable.interface.ts b/src/angular/form-elements/validation/validatable.interface.ts new file mode 100644 index 0000000..6aceafe --- /dev/null +++ b/src/angular/form-elements/validation/validatable.interface.ts @@ -0,0 +1,5 @@ +export interface IValidatableComponent { + + getValue(): any; + +} diff --git a/src/angular/form-elements/validation/validation-group.component.html.ts b/src/angular/form-elements/validation/validation-group.component.html.ts new file mode 100644 index 0000000..dff591e --- /dev/null +++ b/src/angular/form-elements/validation/validation-group.component.html.ts @@ -0,0 +1,3 @@ +export default ` + +`; diff --git a/src/angular/form-elements/validation/validation-group.component.ts b/src/angular/form-elements/validation/validation-group.component.ts new file mode 100644 index 0000000..59ecf4c --- /dev/null +++ b/src/angular/form-elements/validation/validation-group.component.ts @@ -0,0 +1,47 @@ +import { Input, Component, ContentChildren, EventEmitter, Output, QueryList, SimpleChanges, HostBinding, AfterContentInit } from "@angular/core"; +import { AbstractControl, FormControl } from "@angular/forms"; +import { Subscribable } from "rxjs/Observable"; +import { AnonymousSubscription } from "rxjs/Subscription"; +import { IValidator } from './validators/validator.interface'; +import { ValidatorComponent } from './validators/base.validator.component'; +import { RegexValidatorComponent } from './validators/regex.validator.component'; +import { RequiredValidatorComponent } from './validators/required.validator.component'; +import { ValidatableComponent } from './validatable.component'; +import { ValidationComponent } from './validation.component'; +import { CustomValidatorComponent } from './validators/custom.validator.component'; +import template from "./validation.component.html"; + +@Component({ + selector: 'sdc-validation-group', + template +}) +export class ValidationGroupComponent implements AfterContentInit { + + @Input() public disabled: boolean; + @HostBinding('class') classes; + + @ContentChildren(ValidationComponent) public validationsComponents: QueryList; + + private supportedValidator: Array>; + + constructor() { + this.disabled = false; + this.classes = 'sdc-validation-group'; + } + + ngAfterContentInit(): void { + + } + + public validate(): boolean { + let validationResult = true; + // Iterate over all validationComponent inside the group and return boolean result true in case all validations passed. + this.validationsComponents.forEach((validationComponent) => { + if (validationComponent.validate()) { + validationResult = false; + } + }); + return validationResult; + } + +} diff --git a/src/angular/form-elements/validation/validation.component.html.ts b/src/angular/form-elements/validation/validation.component.html.ts new file mode 100644 index 0000000..0f11a23 --- /dev/null +++ b/src/angular/form-elements/validation/validation.component.html.ts @@ -0,0 +1,3 @@ +export default ` + +`; diff --git a/src/angular/form-elements/validation/validation.component.ts b/src/angular/form-elements/validation/validation.component.ts new file mode 100644 index 0000000..4abdd12 --- /dev/null +++ b/src/angular/form-elements/validation/validation.component.ts @@ -0,0 +1,79 @@ +import { Input, Component, ContentChildren, EventEmitter, Output, QueryList, SimpleChanges, HostBinding, AfterContentInit } from "@angular/core"; +import { AbstractControl, FormControl } from "@angular/forms"; +import { Subscribable } from "rxjs/Observable"; +import { AnonymousSubscription } from "rxjs/Subscription"; +import { IValidator } from './validators/validator.interface'; +import { ValidatorComponent } from './validators/base.validator.component'; +import { RegexValidatorComponent } from './validators/regex.validator.component'; +import { RequiredValidatorComponent } from './validators/required.validator.component'; +import { ValidatableComponent } from './validatable.component'; +import { CustomValidatorComponent } from './validators/custom.validator.component'; +import template from "./validation.component.html"; + +@Component({ + selector: 'sdc-validation', + template +}) +export class ValidationComponent implements AfterContentInit { + + @Input() public validateElement: ValidatableComponent; + @Input() public disabled: boolean; + @Output() public validityChanged: EventEmitter = new EventEmitter(); + @HostBinding('class') classes; + + // @ContentChildren does not recieve type any or IValidator or ValidatorComponent, so need to create @ContentChildren for each validator type. + @ContentChildren(RegexValidatorComponent) public regexValidator: QueryList; + @ContentChildren(RequiredValidatorComponent) public requireValidator: QueryList; + @ContentChildren(CustomValidatorComponent) public customValidator: QueryList; + + private supportedValidator: Array>; + + constructor() { + this.disabled = false; + this.classes = 'sdc-validation'; + } + + ngAfterContentInit(): void { + this.supportedValidator = [ + this.regexValidator, + this.requireValidator, + this.customValidator + ]; + + this.validateElement.notifier.subscribe( + (value) => { + const validationResult = this.validateOnChange(value); + this.validateElement.valid = validationResult; + }, + (error) => console.log('Validation subscribe error') + ); + } + + public validate = (): boolean => { + const value = this.validateElement.getValue(); + return this.validateOnChange(value); + } + + private validateOnChange(value: any): boolean { + if (this.disabled) { return true; } + + /** + * Iterate over all validators types (required, regex, etc...), and inside each iterate over + * all validators with same type, and return boolean result true in case all validations passed. + */ + const validationResult: boolean = this.supportedValidator.reduce((sum, validatorName) => { + const response: boolean = validatorName.reduce((_sum, validator) => { + return _sum && validator.validate(value); + }, true); + return sum && response; + }, true); + + if (this.validateElement.valid !== validationResult) { + this.validityChanged.emit(validationResult); + } + + return validationResult; + + } + +} diff --git a/src/angular/form-elements/validation/validation.module.ts b/src/angular/form-elements/validation/validation.module.ts new file mode 100644 index 0000000..4213f76 --- /dev/null +++ b/src/angular/form-elements/validation/validation.module.ts @@ -0,0 +1,35 @@ +import { NgModule } from "@angular/core"; +import { FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { CommonModule } from "@angular/common"; +import { SvgIconModule } from './../../svg-icon/svg-icon.module'; +import { ValidationComponent } from './validation.component'; +import { ValidatorComponent } from './validators/base.validator.component'; +import { RequiredValidatorComponent } from './validators/required.validator.component'; +import { RegexValidatorComponent } from './validators/regex.validator.component'; +import { CustomValidatorComponent } from './validators/custom.validator.component'; +import { ValidationGroupComponent } from './validation-group.component'; + +@NgModule({ + imports: [ + FormsModule, + CommonModule, + ReactiveFormsModule, + SvgIconModule + ], + declarations: [ + ValidationComponent, + RegexValidatorComponent, + RequiredValidatorComponent, + CustomValidatorComponent, + ValidationGroupComponent + ], + exports: [ + ValidationComponent, + RegexValidatorComponent, + RequiredValidatorComponent, + CustomValidatorComponent, + ValidationGroupComponent + ] +}) +export class ValidationModule { +} diff --git a/src/angular/form-elements/validation/validators/base.validator.component.html.ts b/src/angular/form-elements/validation/validators/base.validator.component.html.ts new file mode 100644 index 0000000..aba8eed --- /dev/null +++ b/src/angular/form-elements/validation/validators/base.validator.component.html.ts @@ -0,0 +1,10 @@ +export default ` + + +`; diff --git a/src/angular/form-elements/validation/validators/base.validator.component.ts b/src/angular/form-elements/validation/validators/base.validator.component.ts new file mode 100644 index 0000000..3d751af --- /dev/null +++ b/src/angular/form-elements/validation/validators/base.validator.component.ts @@ -0,0 +1,25 @@ +import { Input, Component, ContentChildren, EventEmitter, Output, QueryList, SimpleChanges, HostBinding } from "@angular/core"; +import { IValidator } from './validator.interface'; +import template from "./base.validator.component.html"; + +@Component({ + selector: 'sdc-validator', + template: template +}) +export abstract class ValidatorComponent { + + @Input() public message: any; + @Input() public disabled: boolean; + @HostBinding('class') classes; + + protected isValid: boolean; + + constructor() { + this.disabled = false; + this.isValid = true; + this.classes = 'sdc-validator sdc-label__error'; + } + + public abstract validate(value: any): boolean; + +} diff --git a/src/angular/form-elements/validation/validators/custom.validator.component.ts b/src/angular/form-elements/validation/validators/custom.validator.component.ts new file mode 100644 index 0000000..eb09636 --- /dev/null +++ b/src/angular/form-elements/validation/validators/custom.validator.component.ts @@ -0,0 +1,23 @@ +import { Input, Component } from "@angular/core"; +import { ValidatorComponent } from "./base.validator.component"; +import { IValidator } from './validator.interface'; +import template from "./base.validator.component.html"; + +@Component({ + selector: 'sdc-custom-validator', + template: template +}) +export class CustomValidatorComponent extends ValidatorComponent implements IValidator { + + @Input() public callback: (...args) => boolean; + + constructor() { + super(); + } + + public validate(value: any): boolean { + this.isValid = this.callback(value); + return this.isValid; + } + +} diff --git a/src/angular/form-elements/validation/validators/regex.validator.component.ts b/src/angular/form-elements/validation/validators/regex.validator.component.ts new file mode 100644 index 0000000..5929016 --- /dev/null +++ b/src/angular/form-elements/validation/validators/regex.validator.component.ts @@ -0,0 +1,24 @@ +import { Input, Component } from "@angular/core"; +import { ValidatorComponent } from "./base.validator.component"; +import { IValidator } from './validator.interface'; +import template from "./base.validator.component.html"; + +@Component({ + selector: 'sdc-regex-validator', + template: template +}) +export class RegexValidatorComponent extends ValidatorComponent implements IValidator { + + @Input() public pattern: RegExp; + + constructor() { + super(); + } + + public validate(value: any): boolean { + const regexp = new RegExp(this.pattern); + this.isValid = regexp.test(value); + return this.isValid; + } + +} diff --git a/src/angular/form-elements/validation/validators/required.validator.component.ts b/src/angular/form-elements/validation/validators/required.validator.component.ts new file mode 100644 index 0000000..7eee932 --- /dev/null +++ b/src/angular/form-elements/validation/validators/required.validator.component.ts @@ -0,0 +1,25 @@ +import { Input, Component } from "@angular/core"; +import { ValidatorComponent } from "./base.validator.component"; +import { IValidator } from './validator.interface'; +import template from "./base.validator.component.html"; + +@Component({ + selector: 'sdc-required-validator', + template: template +}) +export class RequiredValidatorComponent extends ValidatorComponent implements IValidator { + + constructor() { + super(); + } + + public validate(value: any): boolean { + if (value) { + this.isValid = true; + } else { + this.isValid = false; + } + return this.isValid; + } + +} diff --git a/src/angular/form-elements/validation/validators/validator.interface.ts b/src/angular/form-elements/validation/validators/validator.interface.ts new file mode 100644 index 0000000..c0adc24 --- /dev/null +++ b/src/angular/form-elements/validation/validators/validator.interface.ts @@ -0,0 +1,3 @@ +export interface IValidator { + validate(value: any): void; +} diff --git a/src/angular/index.ts b/src/angular/index.ts new file mode 100644 index 0000000..e8a54bd --- /dev/null +++ b/src/angular/index.ts @@ -0,0 +1,68 @@ +import { NgModule } from "@angular/core"; +import { FormElementsModule } from "./form-elements/form-elements.module"; +import { ButtonsModule } from "./buttons/buttons.module"; +import { ModalModule } from "./modals/modal.module"; +import { NotificationModule } from "./notifications/notification.module"; +import { PopupMenuModule } from "./popup-menu/popup-menu.module"; +import { AnimationDirectivesModule } from "./animations/animation-directives.module"; +import { InfiniteScrollModule } from "./infinite-scroll/infinite-scroll.module"; +import { TileModule } from "./tiles/tile.module"; +import { ChecklistModule } from "./checklist/checklist.module"; +import { SvgIconModule } from "./svg-icon/svg-icon.module"; +import { AutoCompleteModule } from "./autocomplete/autocomplete.module"; +import { FilterBarModule } from "./filterbar/filter-bar.module"; +import { SearchBarModule } from "./searchbar/search-bar.module"; +import { TooltipModule } from "./tooltip/tooltip.module"; +import { TagCloudModule } from './tag-cloud/tag-cloud.module'; +import { TabsModule } from "./tabs/tabs.module"; +import { AccordionModule } from "./accordion/accordion.module"; + +@NgModule({ + imports: [ + AnimationDirectivesModule, + ModalModule, + NotificationModule, + FormElementsModule, + ButtonsModule, + PopupMenuModule, + InfiniteScrollModule, + TileModule, + ChecklistModule, + AutoCompleteModule, + FilterBarModule, + SearchBarModule, + TooltipModule, + SvgIconModule, + TagCloudModule, + TabsModule, + AccordionModule + ], + exports: [ + AnimationDirectivesModule, + ModalModule, + NotificationModule, + FormElementsModule, + ButtonsModule, + PopupMenuModule, + InfiniteScrollModule, + TileModule, + ChecklistModule, + AutoCompleteModule, + FilterBarModule, + SearchBarModule, + TooltipModule, + SvgIconModule, + TagCloudModule, + TabsModule, + AccordionModule + ] +}) +export class SdcUiComponentsModule { +} + +import * as SdcUiComponents from './components'; +import * as SdcUiCommon from './common/index'; + +export { SdcUiComponentsNg1Module } from './ng1.module'; +export { SdcUiComponents }; +export { SdcUiCommon }; diff --git a/src/angular/infinite-scroll/infinite-scroll.directive.ts b/src/angular/infinite-scroll/infinite-scroll.directive.ts new file mode 100644 index 0000000..a8ea9f4 --- /dev/null +++ b/src/angular/infinite-scroll/infinite-scroll.directive.ts @@ -0,0 +1,35 @@ +import { Directive, ElementRef, Output, EventEmitter, HostListener, Input } from "@angular/core"; + +@Directive({ + selector: '[infiniteScroll]' +}) +export class InfiniteScrollDirective { + @Input() public infiniteScrollDistance: number = 0; + @Output() public infiniteScroll: EventEmitter; + + private scrollWasHit: boolean = false; + + constructor(private elemRef: ElementRef) { + this.infiniteScroll = new EventEmitter(); + } + + @HostListener('scroll', ['$event']) + public onScroll(evt) { + const scrollContainerElem: HTMLElement = evt.target; + if (scrollContainerElem !== this.elemRef.nativeElement) { + return; + } + + if (scrollContainerElem.scrollTop + scrollContainerElem.clientHeight + this.infiniteScrollDistance >= + scrollContainerElem.scrollHeight) { + // hit only once when entering the distance area from bottom + // (avoid emitting the handler while scrolling in the bottom area) + if (!this.scrollWasHit) { + this.infiniteScroll.emit(); + this.scrollWasHit = true; + } + } else if (this.scrollWasHit) { + this.scrollWasHit = false; + } + } +} diff --git a/src/angular/infinite-scroll/infinite-scroll.module.ts b/src/angular/infinite-scroll/infinite-scroll.module.ts new file mode 100644 index 0000000..8b559ca --- /dev/null +++ b/src/angular/infinite-scroll/infinite-scroll.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from "@angular/core"; +import { InfiniteScrollDirective } from "./infinite-scroll.directive"; + +@NgModule({ + declarations: [ + InfiniteScrollDirective + ], + exports: [ + InfiniteScrollDirective + ], +}) +export class InfiniteScrollModule { +} diff --git a/src/angular/modals/modal-button.component.ts b/src/angular/modals/modal-button.component.ts new file mode 100644 index 0000000..4fa5b7c --- /dev/null +++ b/src/angular/modals/modal-button.component.ts @@ -0,0 +1,29 @@ +import { Component, Input, HostListener } from "@angular/core"; +import { ButtonComponent } from "../buttons/button.component"; +import { ModalService } from "./modal.service"; +import template from "./../buttons/button.component.html"; + +@Component({ + selector: "sdc-modal-button", + template: template +}) +export class ModalButtonComponent extends ButtonComponent { + + @Input() public id?: string; + @Input() public callback: Function; + @Input() public closeModal: boolean; + @HostListener('click') invokeCallback = (): void => { + if (this.callback) { + this.callback(); + } + if (this.closeModal) { + this.modalService.closeModal(); + } + } + + constructor(private modalService: ModalService) { + super(); + this.closeModal = false; + } + +} diff --git a/src/angular/modals/modal-close-button.component.ts b/src/angular/modals/modal-close-button.component.ts new file mode 100644 index 0000000..e761019 --- /dev/null +++ b/src/angular/modals/modal-close-button.component.ts @@ -0,0 +1,34 @@ +import { Component, Input } from "@angular/core"; +import { ButtonComponent } from "../buttons/button.component"; +import { ModalService } from "./modal.service"; +import { RippleAnimationAction } from "../animations/ripple-click.animation.directive"; + +@Component({ + selector: "sdc-modal-close-button", + template: ` +
    + +
    + ` +}) +export class ModalCloseButtonComponent { + + @Input() testId: string; + @Input() disabled: boolean; + + public rippleAnimationAction: RippleAnimationAction = RippleAnimationAction.MOUSE_ENTER; + + constructor(private modalService: ModalService) { + } + + public closeModal = (): void => { + this.modalService.closeModal(); + } + +} diff --git a/src/angular/modals/modal.component.html.ts b/src/angular/modals/modal.component.html.ts new file mode 100644 index 0000000..90119ac --- /dev/null +++ b/src/angular/modals/modal.component.html.ts @@ -0,0 +1,38 @@ +export default ` +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    {{ title }}
    + +
    +
    +
    {{message}}
    +
    +
    + +
    +
    + +`; diff --git a/src/angular/modals/modal.component.spec.ts b/src/angular/modals/modal.component.spec.ts new file mode 100644 index 0000000..372d59d --- /dev/null +++ b/src/angular/modals/modal.component.spec.ts @@ -0,0 +1,105 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { Component, Input, NgModule, ViewContainerRef, Inject, Injectable, Type, ApplicationRef, ComponentFactoryResolver, ComponentRef, EmbeddedViewRef, Injector } from '@angular/core'; +import { NO_ERRORS_SCHEMA } from '@angular/core/src/metadata/ng_module'; +import { ModalService } from './modal.service'; +import { CreateDynamicComponentService } from "../utils/create-dynamic-component.service"; +import { IModalConfig, ModalType, ModalSize } from "../../../src/angular/modals/models/modal-config"; +import { ModalInnerContent } from "../../../stories/ng2-component-lab/components/modal-inner-content-example.component"; + + +describe("Modal unit-tests", () => { + let testService: ModalService; + const testInputModal = { + size: 'xl', //'xl|l|md|sm|xsm' + title: 'Test_Title', + message: 'Test_Message', + modalVisible: true + }; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + providers:[ + ModalService, + { provide : CreateDynamicComponentService, useClass: CreateDynamicComponentServiceTest} + ], + declarations: [], + schemas:[NO_ERRORS_SCHEMA] + }) + testService = TestBed.get(ModalService); + })); + + it('Modal should be open test', () => { + let modalInstance = testService.openModal(testInputModal); + expect(modalInstance).toBeTruthy(); + }) + + it('Modal alert window test', () => { + let modalInstance = testService.openAlertModal('testAlert', 'testMessage'); + expect(modalInstance).toBeTruthy(); + }) + + it('Modal info window test', () => { + let modalInstance = testService.openErrorModal('testMessage', 'sampleTestId'); + expect(modalInstance).toBeTruthy(); + }) + + + it('Custom Modal should be open', () => { + let modalConfig:IModalConfig = { + size: ModalSize.medium, + title: 'Title', + type: ModalType.custom, + buttons: [{text:"Save & Close", closeModal:true}, + {text:"Save", callback:this.customModalOnSave, closeModal:false}, + {text:"Cancel", type: 'secondary', closeModal:true}] + }; + let modalInstance = testService.openCustomModal(modalConfig, ModalInnerContent, {name: "Sample Content"}); + expect(modalInstance).toBeTruthy(); + }) + + it('Shoul close window', () => { + let modalInstance = testService.openModal(testInputModal); + testService.closeModal(); + expect(modalInstance.instance.modalVisible).toBeFalsy(); + }) +}) + + +const testModalInstance = { + instance:{ + closeAnimationComplete:{ + subscribe:() => { + return true; + }, + }, + _createDynamicComponentService:{ + insertComponentDynamically:() => { + return true; + } + }, + modalVisible:true + }, + +}; + +@Component({ + selector: 'modal-test', + template: `
    ` +}) + + + +export class CreateDynamicComponentServiceTest { + modalVisble: true; + public createComponentDynamically = (modalInstance, customData) => { + return testModalInstance; + } + public insertComponentDynamically = () =>{ + return testModalInstance; + } + +} + + + + diff --git a/src/angular/modals/modal.component.ts b/src/angular/modals/modal.component.ts new file mode 100644 index 0000000..4f4d81f --- /dev/null +++ b/src/angular/modals/modal.component.ts @@ -0,0 +1,96 @@ +import { Component, Input, Output, ViewContainerRef, ViewChild, ComponentRef, trigger, state, animate, transition, style, EventEmitter, Renderer, ElementRef } from '@angular/core'; +import { ModalButtonComponent } from './modal-button.component'; +import { LowerCasePipe } from '@angular/common'; +import { ModalCloseButtonComponent } from './modal-close-button.component'; +import template from './modal.component.html'; + +@Component({ + selector: 'sdc-modal', + template: template, + animations: [ + trigger('toggleBackground', [ + transition('* => 1', [style({opacity: 0}), animate('.45s cubic-bezier(0.23, 1, 0.32, 1)')]), + transition('1 => *', [animate('.35s cubic-bezier(0.23, 1, 0.32, 1)', style({opacity: 0}))]) + ]), + trigger('toggleModal', [ + transition('* => 1', [style({opacity: 0, transform: 'translateY(-80px)'}), animate('.45s cubic-bezier(0.23, 1, 0.32, 1)')]), + transition('1 => *', [style({opacity: 1, transform: 'translateY(0px)'}), animate('.35s ease-in-out', style({opacity:0, transform: 'translateY(-80px)'}))]) + ]) + ] +}) + +export class ModalComponent { + + @Input() size: string; 'xl|l|md|sm|xsm'; + @Input() title: string; + @Input() message: string; + @Input() buttons: ModalButtonComponent[]; + @Input() type: string; 'info|error|alert|custom'; + @Input() testId: string; + @Output() closeAnimationComplete: EventEmitter = new EventEmitter(); + + @ViewChild('modalCloseButton') + set refCloseButton(_modalCloseButton: ModalCloseButtonComponent) { + this.modalCloseButton = _modalCloseButton; + } + + modalVisible: boolean; + // Allows for custom component as body instead of simple message. + // See ModalService.createActionModal for implementation details, and HttpService's catchError() for example. + @ViewChild('dynamicContentContainer', {read: ViewContainerRef}) dynamicContentContainer: ViewContainerRef; + innerModalContent: ComponentRef; + + public calculatedTestId: string; + public modalCloseButton: ModalCloseButtonComponent; + + constructor(private renderer: Renderer, + private lowerCasePipe: LowerCasePipe + ) { + this.modalVisible = true; + } + + getCalculatedTestId = (buttonText: string): string => { + // TODO: Replace this + if (this.testId) { + return this.testId + '-' + this.lowerCasePipe.transform(buttonText); + } + return null; + } + + public modalToggled = (toggleEvent: any) => { + if (!toggleEvent.toState) { + this.closeAnimationComplete.emit(); + } + } + + public getCloseButton = (): ModalCloseButtonComponent => { + return this.modalCloseButton; + } + + public getButtonById = (id: string): ModalButtonComponent => { + return this.buttons.find((button) => { + return button.id && button.id === id; + }); + } + + public getButtons = (): ModalButtonComponent[] => { + return this.buttons; + } + + public setButtons = (_buttons: ModalButtonComponent[]): void => { + this.buttons = _buttons; + } + + public getTitle = (): string => { + return this.title; + } + + public setTitle = (_title: string): void => { + this.title = _title; + } + + public hoverAnimation(evn: MouseEvent) { + this.renderer.setElementClass(evn.target as HTMLElement, 'sdc-ripple-click__animated', true); + // evn.taregt.classList.add('sdc-ripple-click__animated'); + } +} diff --git a/src/angular/modals/modal.module.ts b/src/angular/modals/modal.module.ts new file mode 100644 index 0000000..5697437 --- /dev/null +++ b/src/angular/modals/modal.module.ts @@ -0,0 +1,33 @@ +import { NgModule } from "@angular/core"; +import { ModalComponent } from "./modal.component"; +import { ModalService } from "./modal.service"; +import { CommonModule, LowerCasePipe } from "@angular/common"; +import { ButtonsModule } from "../buttons/buttons.module"; +import { AnimationDirectivesModule } from "../animations/animation-directives.module"; +import { CreateDynamicComponentService } from "../utils/create-dynamic-component.service"; +import { ModalButtonComponent } from "./modal-button.component"; +import { ModalCloseButtonComponent } from "./modal-close-button.component"; +import { SvgIconModule } from "../svg-icon/svg-icon.module"; + +@NgModule({ + declarations: [ + ModalComponent, + ModalButtonComponent, + ModalCloseButtonComponent + ], + imports: [ + CommonModule, + ButtonsModule, + AnimationDirectivesModule, + SvgIconModule + ], + entryComponents: [ + ModalComponent, + ModalCloseButtonComponent + ], + exports: [ModalButtonComponent], + providers: [CreateDynamicComponentService, ModalService, LowerCasePipe] +}) +export class ModalModule { + +} diff --git a/src/angular/modals/modal.service.ts b/src/angular/modals/modal.service.ts new file mode 100644 index 0000000..d80ad1f --- /dev/null +++ b/src/angular/modals/modal.service.ts @@ -0,0 +1,100 @@ +import { Injectable, Type, ComponentRef } from '@angular/core'; +import { ModalComponent } from "./modal.component"; +import { CreateDynamicComponentService } from "../utils/create-dynamic-component.service"; +import { IModalConfig, ModalType, ModalSize, IModalButtonComponent } from "./models/modal-config"; + +@Injectable() +export class ModalService { + + private currentModal: ComponentRef; + + constructor(private createDynamicComponentService: CreateDynamicComponentService) { + } + + /* Shortcut method to open an alert modal with title, message, and close button that simply closes the modal. */ + public openAlertModal(title: string, message: string, actionButtonText?: string, actionButtonCallback?: Function, testId?: string) { + const modalConfig = { + size: ModalSize.small, + title: title, + message: message, + testId: testId, + buttons: this.createButtons('secondary', actionButtonText, actionButtonCallback), + type: ModalType.alert + } as IModalConfig; + const modalInstance: ComponentRef = this.openModal(modalConfig); + this.currentModal = modalInstance; + return modalInstance; + } + + public openActionModal = (title: string, message: string, actionButtonText?: string, actionButtonCallback?: Function, testId?: string): ComponentRef => { + const modalConfig = { + size: ModalSize.small, + title: title, + message: message, + testId: testId, + type: ModalType.standard, + buttons: this.createButtons('primary', actionButtonText, actionButtonCallback) + } as IModalConfig; + const modalInstance: ComponentRef = this.openModal(modalConfig); + this.currentModal = modalInstance; + return modalInstance; + } + + public openErrorModal = (errorMessage?: string, testId?: string): ComponentRef => { + const modalConfig = { + size: ModalSize.small, + title: 'Error', + message: errorMessage, + testId: testId, + buttons: [{text: "OK", type: "alert", closeModal: true}], + type: ModalType.error + } as IModalConfig; + const modalInstance: ComponentRef = this.openModal(modalConfig); + this.currentModal = modalInstance; + return modalInstance; + } + + public openCustomModal = (modalConfig: IModalConfig, dynamicComponentType: Type, dynamicComponentInput?: any) => { + const modalInstance: ComponentRef = this.openModal(modalConfig); + this.createInnnerComponent(dynamicComponentType, dynamicComponentInput); + return modalInstance; + } + + public createInnnerComponent = (dynamicComponentType: Type, dynamicComponentInput?: any): void => { + this.currentModal.instance.innerModalContent = this.createDynamicComponentService.insertComponentDynamically(dynamicComponentType, dynamicComponentInput, this.currentModal.instance.dynamicContentContainer); + } + + public openModal = (customModalData: IModalConfig): ComponentRef => { + const modalInstance: ComponentRef = this.createDynamicComponentService.createComponentDynamically(ModalComponent, customModalData); + modalInstance.instance.closeAnimationComplete.subscribe(() => { + this.destroyModal(); + }); + this.currentModal = modalInstance; + return modalInstance; + } + + public getCurrentInstance = () => { + return this.currentModal.instance; + } + + public closeModal = (): void => { // triggers closeModal animation, which then triggers toggleModal.done and the subscription to destroyModal + this.currentModal.instance.modalVisible = false; + } + + private createButtons = (type: string, actionButtonText?: string, actionButtonCallback?: Function): Array => { + const buttons: Array = []; + if (actionButtonText && actionButtonCallback) { + buttons.push({text: actionButtonText, type: type, callback: actionButtonCallback, closeModal: true}); + buttons.push({text: 'Cancel', type: 'secondary', closeModal: true}); + } else { + buttons.push({text: 'Cancel', type: type, closeModal: true}); + } + + return buttons; + } + + private destroyModal = (): void => { + this.currentModal.destroy(); + } + +} diff --git a/src/angular/modals/models/modal-config.ts b/src/angular/modals/models/modal-config.ts new file mode 100644 index 0000000..635942b --- /dev/null +++ b/src/angular/modals/models/modal-config.ts @@ -0,0 +1,44 @@ +import { Placement } from "../../common/enums"; + +export interface IModalConfig { + size?: string; // xl|l|md|sm|xsm + title?: string; + message?: string; + buttons?: IModalButtonComponent[]; + testId?: string; + type?: string; // 'info|error|alert'; +} + +export interface IButtonComponent { + text: string; + disabled?: boolean; + type?: string; + testId?: string; + preventDoubleClick?: boolean; + icon_name?: string; + icon_position?: string; + show_spinner?: boolean; + spinner_position?: Placement; + size?: string; +} + +export interface IModalButtonComponent extends IButtonComponent{ + id?: string; + callback?: Function; + closeModal?: boolean; +} + +export enum ModalType { + alert = "alert", + error = "error", + standard = "info", + custom = "custom" +} + +export enum ModalSize { + xlarge = "xl", + large = "l", + medium = "md", + small = "sm", + xsmall = "xsm" +} diff --git a/src/angular/ng1.module.ts b/src/angular/ng1.module.ts new file mode 100644 index 0000000..6f636f4 --- /dev/null +++ b/src/angular/ng1.module.ts @@ -0,0 +1,135 @@ +import { SdcUiComponentsModule } from './index'; +import { downgradeComponent, downgradeInjectable } from "@angular/upgrade/static"; +import * as Components from './components'; +declare const angular: any; + +let SdcUiComponentsNg1Module = null; + +if (typeof angular !== "undefined") { + + SdcUiComponentsNg1Module = angular.module('SdcUiComponentsNg1', []); + + // // Form Elements + SdcUiComponentsNg1Module.directive('sdcInput', downgradeComponent({ + component: Components.InputComponent, + inputs: ['label', 'value', 'pattern', 'disabled', 'placeHolder', 'required', 'minLength', 'maxLength', 'debounceTime'], + outputs: ['valueChange'] + })); + SdcUiComponentsNg1Module.directive('sdcDropdown', downgradeComponent({ + component: Components.DropDownComponent, + inputs: ['label', 'options', 'disabled', 'placeHolder', 'required', 'validate', 'headless', 'maxHeight', 'selectedOption'], + outputs: ['changeEmitter'] + })); + SdcUiComponentsNg1Module.directive('sdcCheckbox', downgradeComponent({ + component: Components.CheckboxComponent, + inputs: ['label', 'checked', 'disabled'], + outputs: ['checkedChange'] + })); + SdcUiComponentsNg1Module.directive('sdcRadioGroup', downgradeComponent({ + component: Components.RadioGroupComponent, + inputs: ['legend', 'options', 'disabled', 'value', 'direction'], + outputs: ['valueChange'] + })); + + // Buttons + SdcUiComponentsNg1Module.directive('sdcButton', downgradeComponent({ + component: Components.ButtonComponent, + inputs: ['text', 'disabled', 'type', 'size', 'preventDoubleClick', 'icon_name', 'icon_positon'] + })); + + // Modals + SdcUiComponentsNg1Module.service('SdcModalService', downgradeInjectable(Components.ModalService)); + SdcUiComponentsNg1Module.directive('sdcModal', downgradeComponent({ + component: Components.ModalComponent, + inputs: ['size', 'title', 'message', 'buttons', 'type'], + outputs: ['closeAnimationComplete'] + })); + SdcUiComponentsNg1Module.directive('sdcModalButton', downgradeComponent({ + component: Components.ModalButtonComponent, + inputs: ['callback', 'closeModal'] + })); + + // Notifications + SdcUiComponentsNg1Module.service('SdcNotificationService', downgradeInjectable(Components.NotificationsService)); + SdcUiComponentsNg1Module.directive('sdcNotificationContainer', downgradeComponent({ + component: Components.NotificationContainerComponent + })); + SdcUiComponentsNg1Module.directive('sdcNotification', downgradeComponent({ + component: Components.NotificationComponent, + inputs: ['notificationSetting'], + outputs: ['destroyComponent'] + })); + + // Popup Menu + SdcUiComponentsNg1Module.directive('popupMenuList', downgradeComponent({ + component: Components.PopupMenuListComponent, + inputs: ['open', 'position', 'className', 'relative'], + outputs: ['openChange', 'positionChange'] + })); + SdcUiComponentsNg1Module.directive('popupMenuItem', downgradeComponent({ + component: Components.PopupMenuItemComponent, + inputs: ['className', 'type'], + outputs: ['action'] + })); + + // Tiles + SdcUiComponentsNg1Module.directive('sdcTile', downgradeComponent({ + component: Components.TileComponent + })); + SdcUiComponentsNg1Module.directive('sdcTileHeader', downgradeComponent({ + component: Components.TileHeaderComponent + })); + SdcUiComponentsNg1Module.directive('sdcTileContent', downgradeComponent({ + component: Components.TileContentComponent + })); + SdcUiComponentsNg1Module.directive('sdcTileFooter', downgradeComponent({ + component: Components.TileFooterComponent + })); + + // Check List + SdcUiComponentsNg1Module.directive('sdcChecklist', downgradeComponent({ + component: Components.ChecklistComponent, + inputs: ['checklistModel'], + outputs: ['checkedChange'] + })); + + // Tag Cloud + SdcUiComponentsNg1Module.directive('sdcTagCloud', downgradeComponent({ + component: Components.TagCloudComponent, + inputs: ['list', 'isViewOnly', 'isUniqueList', 'uniqueErrorMessage', 'label', 'placeholder'], + outputs: ['listChanged'] + })); + SdcUiComponentsNg1Module.directive('sdcTagItem', downgradeComponent({ + component: Components.TagItemComponent, + inputs: ['text', 'isViewOnly', 'index'], + outputs: ['clickOnDelete'] + })); + + // Tabs + SdcUiComponentsNg1Module.directive('sdcTabs', downgradeComponent({ + component: Components.TabsComponent + })); + SdcUiComponentsNg1Module.directive('sdcTab', downgradeComponent({ + component: Components.TabComponent, + inputs: ['title', 'active'] + })); + + // Svg Icons + SdcUiComponentsNg1Module.directive('svgIcon', downgradeComponent({ + component: Components.SvgIconComponent, + inputs: ['name', 'mode', 'size', 'disabled', 'clickable', 'className'] + })); + SdcUiComponentsNg1Module.directive('svgIconLabel', downgradeComponent({ + component: Components.SvgIconLabelComponent, + inputs: ['name', 'mode', 'size', 'disabled', 'clickable', 'className', 'label', 'labelPlacement', 'labelClassName'] + })); + + // Accordion + SdcUiComponentsNg1Module.directive('sdcAccordion', downgradeComponent({ + component: Components.AccordionComponent, + inputs: ['arrow-direction', 'css-class', 'title', 'open'], + outputs: ['accordionChanged'] + })); +} + +export {SdcUiComponentsNg1Module}; diff --git a/src/angular/notifications/container/notifcontainer.component.html.ts b/src/angular/notifications/container/notifcontainer.component.html.ts new file mode 100644 index 0000000..df08bb4 --- /dev/null +++ b/src/angular/notifications/container/notifcontainer.component.html.ts @@ -0,0 +1,6 @@ +export default ` +
    + + +
    +`; diff --git a/src/angular/notifications/container/notifcontainer.component.ts b/src/angular/notifications/container/notifcontainer.component.ts new file mode 100644 index 0000000..a922dc1 --- /dev/null +++ b/src/angular/notifications/container/notifcontainer.component.ts @@ -0,0 +1,31 @@ +import { Component, Input, Output, EventEmitter, OnInit } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { NotificationSettings } from "../utilities/notification.config"; +import { NotificationsService } from "../services/notifications.service"; +import template from "./notifcontainer.component.html"; + +@Component({ + selector: "sdc-notification-container", + template: template +}) +export class NotificationContainerComponent implements OnInit { + notifications: NotificationSettings[] = []; + + constructor(private notify: NotificationsService) { + } + + public ngOnInit(){ + this.notify.subscribe( (notif : NotificationSettings) => { + this.notifications.push(notif); + }); + } + + + private onDestroyed = (event : any):void =>{ + let index: number = this.notifications.indexOf(event); + if (index !== -1) { + this.notifications.splice(index, 1); + } + } + +} diff --git a/src/angular/notifications/notification-inner-content-example.component.ts b/src/angular/notifications/notification-inner-content-example.component.ts new file mode 100644 index 0000000..552f7b0 --- /dev/null +++ b/src/angular/notifications/notification-inner-content-example.component.ts @@ -0,0 +1,21 @@ +import { Component, Input } from "@angular/core"; + +@Component({ + selector: "innernotif-content", + template: ` +
    +

    Custom Notification

    +
    + {{notifyTitle}} +
    +
    + {{notifyText}} +
    +
    +` +}) +export class InnerNotifContent { + + @Input() notifyTitle:string; + @Input() notifyText:string; +} diff --git a/src/angular/notifications/notification.module.ts b/src/angular/notifications/notification.module.ts new file mode 100644 index 0000000..5891391 --- /dev/null +++ b/src/angular/notifications/notification.module.ts @@ -0,0 +1,29 @@ +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { NotificationComponent } from "./notification/notification.component"; +import { NotificationContainerComponent } from "./container/notifcontainer.component"; +import { NotificationsService } from "./services/notifications.service"; +import { CreateDynamicComponentService } from "../utils/create-dynamic-component.service"; + + +@NgModule({ + declarations: [ + NotificationComponent, + NotificationContainerComponent, + ], + exports: [ + NotificationComponent, + NotificationContainerComponent, + ], + entryComponents: [ + NotificationComponent, + NotificationContainerComponent, + ], + imports: [ + CommonModule + ], + providers: [NotificationsService, CreateDynamicComponentService] +}) +export class NotificationModule { + +} diff --git a/src/angular/notifications/notification/notification.component.html.ts b/src/angular/notifications/notification/notification.component.html.ts new file mode 100644 index 0000000..450972e --- /dev/null +++ b/src/angular/notifications/notification/notification.component.html.ts @@ -0,0 +1,19 @@ +export default ` +
    +
    +
    +
    +
    +
    +
    + {{notificationSetting.notifyTitle}} +
    +
    + {{notificationSetting.notifyText}} +
    +
    +
    +
    +
    +
    +`; diff --git a/src/angular/notifications/notification/notification.component.ts b/src/angular/notifications/notification/notification.component.ts new file mode 100644 index 0000000..476853c --- /dev/null +++ b/src/angular/notifications/notification/notification.component.ts @@ -0,0 +1,42 @@ +import { Component, Input, Output, EventEmitter, OnInit, ViewContainerRef, ViewChild } from "@angular/core"; +import { NotificationSettings } from "../utilities/notification.config"; +import { CreateDynamicComponentService } from "../../utils/create-dynamic-component.service"; +import template from "./notification.component.html"; + +@Component({ + selector: 'sdc-notification', + template: template +}) + +export class NotificationComponent implements OnInit { + + @Input() notificationSetting:NotificationSettings; + @Output() destroyComponent = new EventEmitter(); + @ViewChild("dynamicContentContainer", {read: ViewContainerRef}) contentContainer:ViewContainerRef; + private fade: boolean = false; + + constructor(private createDynamicComponentService: CreateDynamicComponentService) { + } + + public ngOnInit() { + if(this.notificationSetting.hasCustomContent){ + this.createDynamicComponentService.insertComponentDynamically(this.notificationSetting.innerComponentType, this.notificationSetting.innerComponentOptions, this.contentContainer); + } + + if(!this.notificationSetting.sticky){ + setTimeout(() => this.fadeOut(), this.notificationSetting.duration); + } + } + + private fadeOut = ():void => { + this.fade = true; + } + + private destroyMe() { + /*Only destroy on fade out, not on entry animation */ + if(this.fade){ + this.destroyComponent.emit(this.notificationSetting); + } + } + +} diff --git a/src/angular/notifications/services/notifications.service.ts b/src/angular/notifications/services/notifications.service.ts new file mode 100644 index 0000000..28a645c --- /dev/null +++ b/src/angular/notifications/services/notifications.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@angular/core'; +import { NotificationSettings } from '../utilities/notification.config' +import { Subject } from 'rxjs/Subject'; +import { Subscription } from 'rxjs/Subscription'; + + +@Injectable() +export class NotificationsService { + + notifs : NotificationSettings[] = []; + + notifQueue : Subject = new Subject(); + + constructor() {} + + public push(notif : NotificationSettings):void{ + + if( this.notifQueue.observers.length > 0 ) { + this.notifQueue.next(notif); + } else { + this.notifs.push(notif); + } + } + + + + public getNotifications() : NotificationSettings[] { + return this.notifs; + } + + + + public subscribe(observer): Subscription { + let s:Subscription = this.notifQueue.subscribe(observer); + this.notifs.forEach(notif => this.notifQueue.next(notif)); + this.notifs = []; + return s; + } + + +} diff --git a/src/angular/notifications/utilities/notification.config.ts b/src/angular/notifications/utilities/notification.config.ts new file mode 100644 index 0000000..f469b7d --- /dev/null +++ b/src/angular/notifications/utilities/notification.config.ts @@ -0,0 +1,30 @@ +import { Type, ComponentRef } from '@angular/core'; + +export type NotificationType = + "info" | "warn" | "error" | "success"; + +export class NotificationSettings { + + public type: NotificationType; + public notifyText: string; + public notifyTitle: string; + public sticky: boolean; + public hasCustomContent :boolean; + public duration:number; + public innerComponentType: Type; + public innerComponentOptions : any; + + constructor(type: NotificationType, notifyText: string, notifyTitle: string, duration: number = 10000, sticky: boolean = false, hasCustomContent:boolean = false, innerComponentType?:Type, innerComponentOptions? :any) { + + this.type = type; + this.notifyText = notifyText; + this.notifyTitle = notifyTitle; + this.duration = duration; + this.sticky = sticky; + this.hasCustomContent = hasCustomContent; + this.innerComponentType = innerComponentType; + this.innerComponentOptions = innerComponentOptions; + } + + +} diff --git a/src/angular/popup-menu/popup-menu-item.component.spec.ts b/src/angular/popup-menu/popup-menu-item.component.spec.ts new file mode 100644 index 0000000..25b2694 --- /dev/null +++ b/src/angular/popup-menu/popup-menu-item.component.spec.ts @@ -0,0 +1,25 @@ +import { Component, Input, Output, ContentChildren, SimpleChanges, QueryList, EventEmitter, OnChanges, AfterContentInit } from '@angular/core'; +import { FormsModule } from "@angular/forms"; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { PopupMenuItemComponent } from './popup-menu-item.component'; +import { PopupMenuListComponent } from './popup-menu-list.component'; + +describe('Popup Menu', () => { + let component: PopupMenuListComponent; + let fixture: ComponentFixture; + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ PopupMenuListComponent ], + }).compileComponents(); + fixture = TestBed.createComponent(PopupMenuListComponent); + component = fixture.componentInstance; + })); + + it('Popup menu component should be created', () => { + expect(component).toBeTruthy(); + }); + + it('Set Position to Popup Menu', () => { + expect(component.position).toEqual({ x: 0, y: 0 }) + }); +}) diff --git a/src/angular/popup-menu/popup-menu-item.component.ts b/src/angular/popup-menu/popup-menu-item.component.ts new file mode 100644 index 0000000..fb5a71d --- /dev/null +++ b/src/angular/popup-menu/popup-menu-item.component.ts @@ -0,0 +1,34 @@ +import { Component, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core'; +import { PopupMenuListComponent } from "./popup-menu-list.component"; + +@Component({ + selector: 'popup-menu-item', + template: + `
  • + +
  • ` +}) +export class PopupMenuItemComponent { + @Input() public className: string; + @Input() public type: undefined|'disabled'|'selected'|'separator'; + @Output() public action: EventEmitter = new EventEmitter(); + + public parentMenu: PopupMenuListComponent; + public index: number = 0; + + public performAction(evt) { + evt.stopPropagation(); + + if (['disabled', 'separator'].indexOf(this.type) !== -1) { + return; + } + + if (this.parentMenu instanceof PopupMenuListComponent) { + this.parentMenu.open = false; + } + + if (this.action) { + this.action.emit(); + } + } +} diff --git a/src/angular/popup-menu/popup-menu-list.component.ts b/src/angular/popup-menu/popup-menu-list.component.ts new file mode 100644 index 0000000..6a20423 --- /dev/null +++ b/src/angular/popup-menu/popup-menu-list.component.ts @@ -0,0 +1,65 @@ +import { Component, Input, Output, ContentChildren, SimpleChanges, QueryList, EventEmitter, OnChanges, AfterContentInit } from '@angular/core'; +import { PopupMenuItemComponent } from "./popup-menu-item.component"; + +export interface IPoint { + x: number; + y: number; +} + +@Component({ + selector: 'popup-menu-list', + template: + `
      + +
    ` +}) +export class PopupMenuListComponent implements AfterContentInit { + @Input() + public get open(): boolean { + return this._open; + } + public set open(isOpen: boolean) { + isOpen = isOpen !== undefined ? isOpen : false; + if (this._open !== isOpen) { + this._open = isOpen; + this.openChange.emit(this._open); + } + } + @Input() + public get position(): IPoint { + return this._position; + } + public set position(position: IPoint) { + position = position !== undefined ? position : {x: 0, y: 0}; + if (this._position.x !== position.x || this._position.y !== position.y) { + this._position = position; + this.positionChange.emit(this._position); + } + } + @Input() public className: string; + @Input() public relative: boolean = false; + @Output() public openChange: EventEmitter = new EventEmitter(); + @Output() public positionChange: EventEmitter = new EventEmitter(); + + @ContentChildren(PopupMenuItemComponent) private menuItems: QueryList; + + private _open: boolean = false; + private _position: IPoint = {x: 0, y: 0}; + + public ngAfterContentInit() { + this._updateMenuItemsList(this.menuItems); + this.menuItems.changes.subscribe(this._updateMenuItemsList); + } + + private _updateMenuItemsList(menuItemsList: QueryList) { + menuItemsList.forEach((c, idx) => { + c.parentMenu = this; + c.index = idx; + }); + } +} diff --git a/src/angular/popup-menu/popup-menu.module.ts b/src/angular/popup-menu/popup-menu.module.ts new file mode 100644 index 0000000..3a58b91 --- /dev/null +++ b/src/angular/popup-menu/popup-menu.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from "@angular/core"; +import { PopupMenuListComponent } from "./popup-menu-list.component"; +import { PopupMenuItemComponent } from "./popup-menu-item.component"; +import { CommonModule } from "@angular/common"; + + +@NgModule({ + declarations: [ + PopupMenuListComponent, + PopupMenuItemComponent + ], + imports: [ + CommonModule + ], + exports: [ + PopupMenuListComponent, + PopupMenuItemComponent + ], +}) +export class PopupMenuModule { +} diff --git a/src/angular/searchbar/search-bar.component.html.ts b/src/angular/searchbar/search-bar.component.html.ts new file mode 100644 index 0000000..79153f4 --- /dev/null +++ b/src/angular/searchbar/search-bar.component.html.ts @@ -0,0 +1,19 @@ +export default ` +
    + + + + + + + + + + + +
    +`; diff --git a/src/angular/searchbar/search-bar.component.ts b/src/angular/searchbar/search-bar.component.ts new file mode 100644 index 0000000..7f508d7 --- /dev/null +++ b/src/angular/searchbar/search-bar.component.ts @@ -0,0 +1,19 @@ +import { Component, Input, Output, EventEmitter, HostBinding } from '@angular/core'; +import template from "./search-bar.component.html"; + +@Component({ + selector: 'sdc-search-bar', + template: template +}) +export class SearchBarComponent { + + @HostBinding('class') classes = 'sdc-search-bar'; + @Input() public placeholder: string; + @Input() public label: string; + @Input() public searchQuery: string; + @Output() public searchQueryClick: EventEmitter = new EventEmitter(); + + private searchButtonClick = (): void => { + this.searchQueryClick.emit(this.searchQuery); + } +} diff --git a/src/angular/searchbar/search-bar.module.ts b/src/angular/searchbar/search-bar.module.ts new file mode 100644 index 0000000..27750f3 --- /dev/null +++ b/src/angular/searchbar/search-bar.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from "@angular/core"; +import { SearchBarComponent } from "./search-bar.component"; +import { CommonModule } from "@angular/common"; +import { FormElementsModule } from "../form-elements/form-elements.module"; + +@NgModule({ + declarations: [ + SearchBarComponent + ], + imports: [CommonModule, + FormElementsModule], + exports: [ + SearchBarComponent + ], +}) +export class SearchBarModule { +} diff --git a/src/angular/svg-icon/svg-icon-label.component.html.ts b/src/angular/svg-icon/svg-icon-label.component.html.ts new file mode 100644 index 0000000..558b7c4 --- /dev/null +++ b/src/angular/svg-icon/svg-icon-label.component.html.ts @@ -0,0 +1,6 @@ +export default ` +
    + + {{ label }} +
    +`; diff --git a/src/angular/svg-icon/svg-icon-label.component.ts b/src/angular/svg-icon/svg-icon-label.component.ts new file mode 100644 index 0000000..5a00c3d --- /dev/null +++ b/src/angular/svg-icon/svg-icon-label.component.ts @@ -0,0 +1,26 @@ +import { Component, Input } from "@angular/core"; +import { SvgIconComponent } from './svg-icon.component'; +import { Mode, Size, Placement } from "../common/enums"; +import { DomSanitizer, SafeHtml } from "@angular/platform-browser"; +import template from './svg-icon-label.component.html'; + +@Component({ + selector: 'svg-icon-label', + template: template, + styles: [` + :host { + display: inline-flex; + } + `] +}) +export class SvgIconLabelComponent extends SvgIconComponent { + + @Input() public label: string; + @Input() public labelPlacement: Placement; + @Input() public labelClassName: string; + + constructor(protected domSanitizer: DomSanitizer) { + super(domSanitizer); + this.labelPlacement = Placement.left; + } +} diff --git a/src/angular/svg-icon/svg-icon.component.html.ts b/src/angular/svg-icon/svg-icon.component.html.ts new file mode 100644 index 0000000..1baedbd --- /dev/null +++ b/src/angular/svg-icon/svg-icon.component.html.ts @@ -0,0 +1,3 @@ +export default ` +
    +`; diff --git a/src/angular/svg-icon/svg-icon.component.ts b/src/angular/svg-icon/svg-icon.component.ts new file mode 100644 index 0000000..d53981d --- /dev/null +++ b/src/angular/svg-icon/svg-icon.component.ts @@ -0,0 +1,77 @@ +import { Component, Input, OnChanges, SimpleChanges, HostBinding } from "@angular/core"; +import { DomSanitizer, SafeHtml } from "@angular/platform-browser"; +import { Mode, Size } from "../common/enums"; +import iconsMap from '../../common/icons-map'; +import template from './svg-icon.component.html'; + +@Component({ + selector: 'svg-icon', + template: template, + styles: [` + :host { + display: inline-flex; + } + `] +}) +export class SvgIconComponent implements OnChanges { + + @Input() public name: string; + @Input() public mode: Mode; + @Input() public size: Size; + @Input() public disabled: boolean; + @Input() public clickable: boolean; + @Input() public className: any; + + public svgIconContent: string; + public svgIconContentSafeHtml: SafeHtml; + public svgIconCustomClassName: string; + private classes: string; + + constructor(protected domSanitizer: DomSanitizer) { + this.size = Size.medium; + this.disabled = false; + } + + static get Icons(): {[key: string]: string} { + return iconsMap; + } + + public ngOnChanges(changes: SimpleChanges) { + if (changes.name) { + this.updateSvgIconByName(); + this.buildClasses(); + } + } + + protected updateSvgIconByName() { + this.svgIconContent = SvgIconComponent.Icons[this.name] || null; + if (this.svgIconContent) { + this.svgIconContentSafeHtml = this.domSanitizer.bypassSecurityTrustHtml(this.svgIconContent); + this.svgIconCustomClassName = '__' + this.name.replace(/\s+/g, '_'); + } else { + this.svgIconContentSafeHtml = null; + this.svgIconCustomClassName = 'missing'; + } + } + + private buildClasses = (): void => { + const _classes = []; + _classes.push('svg-icon'); + if (this.mode) { + _classes.push('mode-' + this.mode); + } + if (this.size) { + _classes.push('size-' + this.size); + } + if (this.clickable) { + _classes.push('clickable'); + } + if (this.svgIconCustomClassName) { + _classes.push(this.svgIconCustomClassName); + } + if (this.className) { + _classes.push(this.className); + } + this.classes = _classes.join(" "); + } +} diff --git a/src/angular/svg-icon/svg-icon.module.ts b/src/angular/svg-icon/svg-icon.module.ts new file mode 100644 index 0000000..87a0d86 --- /dev/null +++ b/src/angular/svg-icon/svg-icon.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { SvgIconComponent } from "./svg-icon.component"; +import { SvgIconLabelComponent } from "./svg-icon-label.component"; + +@NgModule({ + declarations: [ + SvgIconComponent, + SvgIconLabelComponent + ], + imports: [ + CommonModule + ], + exports: [ + SvgIconComponent, + SvgIconLabelComponent + ], +}) + +export class SvgIconModule { +} diff --git a/src/angular/tabs/children/tab.component.html.ts b/src/angular/tabs/children/tab.component.html.ts new file mode 100644 index 0000000..36ff413 --- /dev/null +++ b/src/angular/tabs/children/tab.component.html.ts @@ -0,0 +1,5 @@ +export default ` +
    + +
    +`; diff --git a/src/angular/tabs/children/tab.component.ts b/src/angular/tabs/children/tab.component.ts new file mode 100644 index 0000000..3b96e87 --- /dev/null +++ b/src/angular/tabs/children/tab.component.ts @@ -0,0 +1,16 @@ +import { Component, Input } from '@angular/core'; +import { Mode } from './../../common/enums'; +import template from "./tab.component.html"; + +@Component({ + selector: 'sdc-tab', + template: template +}) +export class TabComponent { + @Input() public title: string; + @Input() public titleIcon: string; + @Input() public active = false; + + public titleIconMode = Mode.secondary; + +} diff --git a/src/angular/tabs/tabs.component.html.ts b/src/angular/tabs/tabs.component.html.ts new file mode 100644 index 0000000..2333b86 --- /dev/null +++ b/src/angular/tabs/tabs.component.html.ts @@ -0,0 +1,14 @@ +export default ` +
      + +
    + +`; diff --git a/src/angular/tabs/tabs.component.ts b/src/angular/tabs/tabs.component.ts new file mode 100644 index 0000000..595f304 --- /dev/null +++ b/src/angular/tabs/tabs.component.ts @@ -0,0 +1,41 @@ +import { Component, Input, AfterContentInit, ContentChildren, QueryList, HostBinding } from '@angular/core'; +import { TabComponent } from './children/tab.component'; +import { SvgIconComponent } from "./../../../src/angular/svg-icon/svg-icon.component"; +import { Mode, Placement, Size } from './../common/enums'; +import template from "./tabs.component.html"; + +@Component({ + selector: 'sdc-tabs', + template: template +}) + +export class TabsComponent implements AfterContentInit { + + @HostBinding('class') classes = 'sdc-tabs sdc-tabs-header'; + @ContentChildren(TabComponent) private tabs: QueryList; + + public _size = Size.medium; + + public selectTab(tab: TabComponent) { + // deactivate all tabs + this.tabs.toArray().forEach((_tab: TabComponent) => { + _tab.active = false; + _tab.titleIconMode = Mode.secondary; + }); + + // activate the tab the user has clicked on. + tab.active = true; + tab.titleIconMode = Mode.primary; + } + + public ngAfterContentInit() { + // get all active tabs + const activeTabs = this.tabs.filter((tab) => tab.active); + + // if there is no active tab set, activate the first + if (activeTabs.length === 0) { + this.selectTab(this.tabs.first); + } + } + + } diff --git a/src/angular/tabs/tabs.module.ts b/src/angular/tabs/tabs.module.ts new file mode 100644 index 0000000..107942d --- /dev/null +++ b/src/angular/tabs/tabs.module.ts @@ -0,0 +1,23 @@ +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { FormElementsModule } from "../form-elements/form-elements.module"; +import { TabsComponent } from "./tabs.component"; +import { TabComponent } from './children/tab.component'; +import { SvgIconModule } from './../svg-icon/svg-icon.module'; + +@NgModule({ + declarations: [ + TabsComponent, + TabComponent + ], + imports: [ + CommonModule, + SvgIconModule + ], + exports: [ + TabsComponent, + TabComponent + ] +}) +export class TabsModule { +} diff --git a/src/angular/tag-cloud/tag-cloud.component.html.ts b/src/angular/tag-cloud/tag-cloud.component.html.ts new file mode 100644 index 0000000..2ff4e8a --- /dev/null +++ b/src/angular/tag-cloud/tag-cloud.component.html.ts @@ -0,0 +1,30 @@ +export default ` +
    + +
    + + + + + + + + + + +
    +
    +
    + +
    +
    {{uniqueErrorMessage}}
    +`; diff --git a/src/angular/tag-cloud/tag-cloud.component.ts b/src/angular/tag-cloud/tag-cloud.component.ts new file mode 100644 index 0000000..1635b8d --- /dev/null +++ b/src/angular/tag-cloud/tag-cloud.component.ts @@ -0,0 +1,46 @@ +import { Component, EventEmitter, Input, Output } from "@angular/core"; +import template from "./tag-cloud.component.html"; + +@Component({ + selector: 'sdc-tag-cloud', + template: template, +}) +export class TagCloudComponent { + @Input() public list: string[]; + @Input() public isViewOnly: boolean|number[]; // get a boolean parameter or array of specific items indexes. + @Input() public isUniqueList: boolean; + @Input() public uniqueErrorMessage: string = "Unique error"; + @Input() public label: string; + @Input() public placeholder: string; + @Output() public listChanged: EventEmitter = new EventEmitter(); + private newTagItem: string; + private uniqueError: boolean; + + private onKeyup = (e): void => { + if (e.keyCode === 13) { + this.insertItemToList(); + } + } + + private insertItemToList = (): void => { + this.validateTag(); + if (!this.uniqueError && this.newTagItem.length) { + this.list.push(this.newTagItem); + this.newTagItem = ""; + this.listChanged.emit(this.list); + } + } + + private deleteItemFromList = (index: number): void => { + this.list.splice(index, 1); + if (Array.isArray(this.isViewOnly)) { + this.isViewOnly = this.isViewOnly.map((i: number) => { + return i > index ? i - 1 : i; + }); + } + } + + private validateTag = (): void => { + this.uniqueError = this.list && this.list.indexOf(this.newTagItem) > -1; + } +} diff --git a/src/angular/tag-cloud/tag-cloud.module.ts b/src/angular/tag-cloud/tag-cloud.module.ts new file mode 100644 index 0000000..fd7efb4 --- /dev/null +++ b/src/angular/tag-cloud/tag-cloud.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from "@angular/core"; +import { TagItemComponent } from "./tag-item/tag-item.component"; +import { TagCloudComponent } from "./tag-cloud.component"; +import { CommonModule } from "@angular/common"; +import { FormElementsModule } from './../form-elements/form-elements.module'; + +@NgModule({ + declarations: [ + TagItemComponent, + TagCloudComponent + ], + imports: [ + CommonModule, + FormElementsModule + ], + exports: [ + TagCloudComponent + ] +}) +export class TagCloudModule { +} diff --git a/src/angular/tag-cloud/tag-item/tag-item.component.html.ts b/src/angular/tag-cloud/tag-item/tag-item.component.html.ts new file mode 100644 index 0000000..04112c1 --- /dev/null +++ b/src/angular/tag-cloud/tag-item/tag-item.component.html.ts @@ -0,0 +1,16 @@ +export default ` +
    + {{text}} + + + + + + + + + + +
    +`; + diff --git a/src/angular/tag-cloud/tag-item/tag-item.component.ts b/src/angular/tag-cloud/tag-item/tag-item.component.ts new file mode 100644 index 0000000..f2e2fa7 --- /dev/null +++ b/src/angular/tag-cloud/tag-item/tag-item.component.ts @@ -0,0 +1,15 @@ +import { Component, EventEmitter, Input, Output, HostBinding } from "@angular/core"; +import template from "./tag-item.component.html"; + +@Component({ + selector: 'sdc-tag-item', + template: template +}) + +export class TagItemComponent { + @HostBinding('class') classes = 'sdc-tag-item'; + @Input() public text: string; + @Input() public isViewOnly: boolean; + @Input() public index: number; + @Output() public clickOnDelete: EventEmitter = new EventEmitter(); +} diff --git a/src/angular/tiles/children/tile-content.component.ts b/src/angular/tiles/children/tile-content.component.ts new file mode 100644 index 0000000..741db26 --- /dev/null +++ b/src/angular/tiles/children/tile-content.component.ts @@ -0,0 +1,10 @@ +import { Component, HostBinding } from "@angular/core"; + +@Component({ + selector: 'sdc-tile-content', + template: '' +}) + +export class TileContentComponent { + @HostBinding('class') classes = 'sdc-tile-content'; +} diff --git a/src/angular/tiles/children/tile-footer.component.ts b/src/angular/tiles/children/tile-footer.component.ts new file mode 100644 index 0000000..519c5ba --- /dev/null +++ b/src/angular/tiles/children/tile-footer.component.ts @@ -0,0 +1,10 @@ +import { Component, HostBinding } from '@angular/core'; + +@Component({ + selector: 'sdc-tile-footer', + template: '' +}) + +export class TileFooterComponent { + @HostBinding('class') classes = 'sdc-tile-footer'; +} diff --git a/src/angular/tiles/children/tile-header.component.ts b/src/angular/tiles/children/tile-header.component.ts new file mode 100644 index 0000000..b040bcb --- /dev/null +++ b/src/angular/tiles/children/tile-header.component.ts @@ -0,0 +1,10 @@ +import { Component, HostBinding } from '@angular/core'; + +@Component({ + selector: "sdc-tile-header", + template: '' +}) + +export class TileHeaderComponent { + @HostBinding('class') classes = 'sdc-tile-header'; +} diff --git a/src/angular/tiles/tile.component.html.ts b/src/angular/tiles/tile.component.html.ts new file mode 100644 index 0000000..81803d5 --- /dev/null +++ b/src/angular/tiles/tile.component.html.ts @@ -0,0 +1,5 @@ +export default ` + + + +`; diff --git a/src/angular/tiles/tile.component.ts b/src/angular/tiles/tile.component.ts new file mode 100644 index 0000000..3791ca0 --- /dev/null +++ b/src/angular/tiles/tile.component.ts @@ -0,0 +1,11 @@ +import { Component, HostBinding } from '@angular/core'; +import template from "./tile.component.html"; + +@Component({ + selector: "sdc-tile", + template: template +}) + +export class TileComponent { + @HostBinding('class') classes = 'sdc-tile'; +} diff --git a/src/angular/tiles/tile.module.ts b/src/angular/tiles/tile.module.ts new file mode 100644 index 0000000..43c750b --- /dev/null +++ b/src/angular/tiles/tile.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from "@angular/core"; +import { TileComponent } from "./tile.component"; +import { CommonModule } from "@angular/common"; +import { TileContentComponent } from "./children/tile-content.component"; +import { TileFooterComponent } from "./children/tile-footer.component"; +import { TileHeaderComponent } from "./children/tile-header.component"; + +@NgModule({ + declarations: [ + TileComponent, + TileContentComponent, + TileFooterComponent, + TileHeaderComponent + ], + imports: [CommonModule], + entryComponents: [TileComponent], + exports: [ + TileComponent, + TileContentComponent, + TileFooterComponent, + TileHeaderComponent + ] +}) + +export class TileModule { + +} diff --git a/src/angular/tooltip/tooltip-template.component.ts b/src/angular/tooltip/tooltip-template.component.ts new file mode 100644 index 0000000..7cb7f72 --- /dev/null +++ b/src/angular/tooltip/tooltip-template.component.ts @@ -0,0 +1,20 @@ +import { Component, ViewChild, ViewContainerRef, AfterViewInit } from '@angular/core'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; + +@Component({ + selector: 'tooltip-template', + template: ` +
    + +
    ` +}) + +export class TooltipTemplateComponent implements AfterViewInit { + @ViewChild('templateContainer', {read: ViewContainerRef}) public container: ViewContainerRef; + + public viewReady: BehaviorSubject = new BehaviorSubject(false); + + ngAfterViewInit() : void { + this.viewReady.next(true); + } +} diff --git a/src/angular/tooltip/tooltip.directive.ts b/src/angular/tooltip/tooltip.directive.ts new file mode 100644 index 0000000..77cec62 --- /dev/null +++ b/src/angular/tooltip/tooltip.directive.ts @@ -0,0 +1,459 @@ +import { Directive, ElementRef, HostListener, OnInit, Input, Renderer, TemplateRef } from '@angular/core'; +import { TooltipTemplateComponent } from './tooltip-template.component'; +import { CreateDynamicComponentService } from '../utils/create-dynamic-component.service'; + +const pixel = 'px'; +const leftStyle = 'left'; +const topStyle = 'top'; +const showSuffix = 'show'; +const rightBottomSuffix = 'right__bottom'; +const centerMiddleSuffix = 'center__middle'; + +@Directive({ + selector: '[sdc-tooltip]' +}) +export class TooltipDirective implements OnInit { + @Input('tooltip-text') public text = 'tooltip'; + @Input('tooltip-placement') public placement: TooltipPlacement = TooltipPlacement.Top; + @Input('tooltip-css-class') public customCssClass: string; + @Input('tooltip-template') public template: TemplateRef; + @Input('tooltip-arrow-offset') public arrowOffset: number = 10; + @Input('tooltip-arrow-placement') public arrowPlacement: ArrowPlacement = ArrowPlacement.LeftTop; + @Input('tooltip-offset') public tooltipOffset: number = 3; + + private cssClass: string = 'sdc-tooltip'; // default css class + private tooltip: any; // tooltip html element + private elemPosition: any; + private tooltipTemplateContainer: any; + + private scrollEventHandler = () => {}; + + constructor( + private elementRef: ElementRef, + private service: CreateDynamicComponentService, + private renderer: Renderer) { + + this.elementRef.nativeElement.title = ""; + } + + @HostListener('mouseenter') + public onMouseEnter() { + this.show(); + this.activateScrollEvent(); + } + + @HostListener('mouseleave') + public onMouseLeave() { + this.hide(); + this.deactivateScrollEvent(); + } + + ngOnInit(): void { + this.initScrollEvent(); + } + + private get ScreenWidth() { + return document.documentElement.clientWidth; + } + + private get ScreenHeight() { + return document.documentElement.clientHeight; + } + + private create() { + this.tooltipTemplateContainer = this.service.createComponentDynamically(TooltipTemplateComponent, document.body); + + /** + * Creating a view (injecting our template) from template in our component. + */ + this.tooltip = this.tooltipTemplateContainer.location.nativeElement.querySelector( + '.sdc-tooltip-template-container'); + + if (this.template) { + this.tooltipTemplateContainer.instance.container.createEmbeddedView(this.template); + } else { + this.tooltip.textContent = this.text ? this.text : 'tooltip'; + } + + this.setCssClass(true); + } + + private destroy() { + this.tooltipTemplateContainer.destroy(); + this.tooltip = null; + } + + private show() { + this.create(); + + /** + * View is ready (AfterViewInit event in template component) + */ + this.tooltipTemplateContainer.instance.viewReady.subscribe((isReady) => { + if (isReady) { + this.setPosition(); + this.toggleShowCssClass(true); // add css class + } + }); + } + + private hide() { + this.toggleShowCssClass(false); // remove css class + + this.destroy(); + } + + private toggleShowCssClass(isAdd: boolean) { + if (this.tooltip) { + this.setCssClass(isAdd, '-' + showSuffix); + } + } + + /** + * Adds placement css class and sets tooltip position in style + */ + private setPosition() { + const tooltipPos: IPlacementData = this.getPlacementData(); + + const placementSuffix: string = TooltipPlacement[tooltipPos.placement].toLowerCase(); + + this.setCssClass(true, '-' + placementSuffix); + + this.setAdditionalCssClass(placementSuffix); + + this.renderer.setElementStyle(this.tooltip, topStyle, tooltipPos.top + pixel); + this.renderer.setElementStyle(this.tooltip, leftStyle, tooltipPos.left + pixel); + } + + private setAdditionalCssClass(placementSuffix: string) { + if (this.arrowPlacement === ArrowPlacement.RightBottom) { + this.setCssClass(true, '-' + placementSuffix + '-' + rightBottomSuffix); + } else if (this.arrowPlacement === ArrowPlacement.CenterMiddle) { + this.setCssClass(true, '-' + placementSuffix + '-' + centerMiddleSuffix); + } + } + + private setCssClass(isAdd: boolean, suffix: string = '') { + this.renderer.setElementClass(this.tooltip, this.cssClass + suffix, isAdd); + + if (this.customCssClass) { + this.renderer.setElementClass(this.tooltip, this.customCssClass + suffix, isAdd); + } + } + + /** + * Checks the specified placement (first element in array), if it is not valid - checks other placements + * @returns {IPlacementData} + */ + private getPlacementData(): IPlacementData { + const placement: TooltipPlacement = this.placement; + let tooltipPos: IPlacementData; + + const tooltipPosWithPlacement = this.getPlacement.bind(this, placement); + + // TODO add comments - done + switch (placement) { + case TooltipPlacement.Left: + tooltipPos = tooltipPosWithPlacement( + TooltipPlacement.Right, + TooltipPlacement.Top, + TooltipPlacement.Bottom); + break; + + case TooltipPlacement.Right: + tooltipPos = tooltipPosWithPlacement( + TooltipPlacement.Left, + TooltipPlacement.Top, + TooltipPlacement.Bottom); + break; + + case TooltipPlacement.Top: + tooltipPos = tooltipPosWithPlacement( + TooltipPlacement.Bottom, + TooltipPlacement.Left, + TooltipPlacement.Right); + break; + + case TooltipPlacement.Bottom: + tooltipPos = tooltipPosWithPlacement( + TooltipPlacement.Top, + TooltipPlacement.Left, + TooltipPlacement.Right); + break; + } + + return tooltipPos; + } + + /** + * Returns valid tooltip position data + * @param {TooltipPlacement} placement + * @param {TooltipPlacement} additionalPlacements + * @returns {IPlacementData} + */ + private getPlacement(placement: TooltipPlacement, + ...additionalPlacements: TooltipPlacement[], + ): IPlacementData { + const placements: TooltipPlacement[] = [placement, ...additionalPlacements]; + const filterPlacements = placements + .map((pl) => this.getPosition(pl)) + .filter((item) => this.validatePosition(item)); + return filterPlacements.length > 0 ? filterPlacements[0] : this.getPosition(placement); + } + + /** + * Returns input data for getPosition method + * @returns {ITooltipPositionParams} + */ + private getPlacementInputParams(): ITooltipPositionParams { + this.elemPosition = this.elementRef.nativeElement.getBoundingClientRect(); + + return { + elemHeight: this.elementRef.nativeElement.offsetHeight, + elemLeft: this.elemPosition.left, + elemTop: this.elemPosition.top, + elemWidth: this.elementRef.nativeElement.offsetWidth, + pageYOffset: window.pageYOffset, + tooltipHeight: this.tooltip.offsetHeight, // .clientHeight, + tooltipOffset: this.tooltipOffset, + tooltipWidth: this.tooltip.offsetWidth, + arrowOffset: this.arrowOffset + }; + } + + /** + * Returns tooltip position data + * @param {TooltipPlacement} placement (left, top, right, bottom) + * @returns {IPlacementData} + */ + private getPosition(placement: TooltipPlacement): IPlacementData { + switch(this.arrowPlacement) { + case ArrowPlacement.LeftTop: + return this.getLeftTopPosition(placement); + + case ArrowPlacement.RightBottom: + return this.getRightBottomPosition(placement); + } + + return this.getCenterMiddlePosition(placement); + } + + /** + * Returns tooltip position data (center / middle arrow) + * @param {TooltipPlacement} placement (left, top, right, bottom) + * @returns {IPlacementData} + */ + private getCenterMiddlePosition(placement: TooltipPlacement): IPlacementData { + let left = 0; + let top = 0; + + const inputPos: ITooltipPositionParams = this.getPlacementInputParams(); + switch (placement) { + case TooltipPlacement.Left: + left = inputPos.elemLeft - inputPos.tooltipWidth - inputPos.tooltipOffset; + top = inputPos.elemTop + inputPos.pageYOffset + inputPos.elemHeight / 2 - inputPos.tooltipHeight / 2; + break; + + case TooltipPlacement.Right: + left = inputPos.elemLeft + inputPos.elemWidth + inputPos.tooltipOffset; + top = inputPos.elemTop + inputPos.pageYOffset + inputPos.elemHeight / 2 - inputPos.tooltipHeight / 2; + break; + + case TooltipPlacement.Top: + left = inputPos.elemLeft + inputPos.elemWidth / 2 - inputPos.tooltipWidth / 2; + top = inputPos.elemTop + inputPos.pageYOffset - inputPos.tooltipHeight - inputPos.tooltipOffset; + break; + + case TooltipPlacement.Bottom: + left = inputPos.elemLeft + inputPos.elemWidth / 2 - inputPos.tooltipWidth / 2; + top = inputPos.elemTop + inputPos.pageYOffset + inputPos.elemHeight + inputPos.tooltipOffset; + break; + } + + return { + height: inputPos.tooltipHeight, + left, + placement, + top, + width: inputPos.tooltipWidth, + pageYOffset: inputPos.pageYOffset + } as IPlacementData; + } + + /** + * Returns tooltip position data (left / top arrow) + * @param {TooltipPlacement} placement (left, top, right, bottom) + * @returns {IPlacementData} + */ + private getLeftTopPosition(placement: TooltipPlacement): IPlacementData { + let left = 0; + let top = 0; + + const inputPos: ITooltipPositionParams = this.getPlacementInputParams(); + switch (placement) { + case TooltipPlacement.Left: + left = inputPos.elemLeft - inputPos.tooltipWidth - inputPos.tooltipOffset; + top = inputPos.elemTop + inputPos.pageYOffset + inputPos.elemHeight / 2 - inputPos.arrowOffset; + break; + + case TooltipPlacement.Right: + left = inputPos.elemLeft + inputPos.elemWidth + inputPos.tooltipOffset; + top = inputPos.elemTop + inputPos.pageYOffset + inputPos.elemHeight / 2 - inputPos.arrowOffset; + break; + + case TooltipPlacement.Top: + left = inputPos.elemLeft + inputPos.elemWidth / 2 - inputPos.arrowOffset; + top = inputPos.elemTop + inputPos.pageYOffset - inputPos.tooltipHeight - inputPos.tooltipOffset; + break; + + case TooltipPlacement.Bottom: + left = inputPos.elemLeft + inputPos.elemWidth / 2 - inputPos.arrowOffset; + top = inputPos.elemTop + inputPos.pageYOffset + inputPos.elemHeight + inputPos.tooltipOffset; + break; + } + + return { + height: inputPos.tooltipHeight, + left, + placement, + top, + width: inputPos.tooltipWidth, + pageYOffset: inputPos.pageYOffset + } as IPlacementData; + } + + /** + * Returns tooltip position data (right / bottom arrow) + * @param {TooltipPlacement} placement (left, top, right, bottom) + * @returns {IPlacementData} + */ + private getRightBottomPosition(placement: TooltipPlacement): IPlacementData { + let left = 0; + let top = 0; + + const inputPos: ITooltipPositionParams = this.getPlacementInputParams(); + switch (placement) { + case TooltipPlacement.Left: + left = inputPos.elemLeft - inputPos.tooltipWidth - inputPos.tooltipOffset; + top = inputPos.elemTop + inputPos.pageYOffset + inputPos.elemHeight / 2 - inputPos.tooltipHeight + inputPos.arrowOffset; + break; + + case TooltipPlacement.Right: + left = inputPos.elemLeft + inputPos.elemWidth + inputPos.tooltipOffset; + top = inputPos.elemTop + inputPos.pageYOffset + inputPos.elemHeight / 2 - inputPos.tooltipHeight + inputPos.arrowOffset; + break; + + case TooltipPlacement.Top: + left = inputPos.elemLeft + inputPos.elemWidth / 2 - inputPos.tooltipWidth + inputPos.arrowOffset; + top = inputPos.elemTop + inputPos.pageYOffset - inputPos.tooltipHeight - inputPos.tooltipOffset; + break; + + case TooltipPlacement.Bottom: + left = inputPos.elemLeft + inputPos.elemWidth / 2 - inputPos.tooltipWidth + inputPos.arrowOffset; + top = inputPos.elemTop + inputPos.pageYOffset + inputPos.elemHeight + inputPos.tooltipOffset; + break; + } + + return { + height: inputPos.tooltipHeight, + left, + placement, + top, + width: inputPos.tooltipWidth, + pageYOffset: inputPos.pageYOffset + } as IPlacementData; + } + + /** + * Checks if tooltip position is valid + * @param {IPlacementData} pos + * @returns {boolean} + */ + private validatePosition(pos: IPlacementData): boolean { + if (pos.left < 0 || pos.left + pos.width - 1 > this.ScreenWidth) { + return false; + } + + if (pos.top - pos.pageYOffset < 0 || pos.top - pos.pageYOffset + pos.height - 1 > this.ScreenHeight) { + return false; + } + + return true; + } + + /** + * Scrolling + */ + + private debounce(func: Function, wait: number, immediate?: boolean) { + let timeout; + return function() { + const context = this; + const args = arguments; + const later = () => { + timeout = null; + if (!immediate) { + func.apply(context, args); + } + }; + const callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + func.apply(context, args); + } + }; + } + + private initScrollEvent() { + this.scrollEventHandler = this.debounce(() => { + try { + this.setPosition(); + } catch (e) { + + } + }, 10); + } + + private activateScrollEvent() { + window.addEventListener('scroll', this.scrollEventHandler , true); + } + + private deactivateScrollEvent() { + window.removeEventListener('scroll', this.scrollEventHandler , true); + } +} + +export enum TooltipPlacement { + Left, + Right, + Top, + Bottom +} + +export enum ArrowPlacement { + CenterMiddle, + LeftTop, + RightBottom +} + +interface ITooltipPositionParams { + elemLeft: number; + elemTop: number; + elemWidth: number; + elemHeight: number; + tooltipWidth: number; + tooltipHeight: number; + tooltipOffset: number; + pageYOffset: number; + arrowOffset: number; +} + +interface IPlacementData { + left: number; + top: number; + width: number; + height: number; + pageYOffset: number; + placement?: TooltipPlacement; +} diff --git a/src/angular/tooltip/tooltip.module.ts b/src/angular/tooltip/tooltip.module.ts new file mode 100644 index 0000000..a4ad86d --- /dev/null +++ b/src/angular/tooltip/tooltip.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { TooltipDirective } from './tooltip.directive'; +import { TooltipTemplateComponent } from './tooltip-template.component'; + +@NgModule({ + declarations: [ + TooltipDirective, + TooltipTemplateComponent + ], + imports: [], + entryComponents: [TooltipTemplateComponent], + exports: [ + TooltipDirective + ], +}) +export class TooltipModule { +} diff --git a/src/angular/utils/create-dynamic-component.service.ts b/src/angular/utils/create-dynamic-component.service.ts new file mode 100644 index 0000000..428dd73 --- /dev/null +++ b/src/angular/utils/create-dynamic-component.service.ts @@ -0,0 +1,101 @@ +import { Injectable, Type, ApplicationRef, ComponentFactoryResolver, ComponentRef, EmbeddedViewRef, Injector } from '@angular/core'; +import { ViewContainerRef } from '@angular/core/src/linker/view_container_ref'; + +@Injectable() +export class CreateDynamicComponentService { + + constructor(private componentFactoryResolver: ComponentFactoryResolver, + private applicationRef: ApplicationRef, + private injector: Injector) { + } + + /** + * Gets the root view container to inject the component to. + * + * @returns {ComponentRef} + * + * @memberOf InjectionService + */ + private getRootViewContainer(): ComponentRef { + const rootComponents = this.applicationRef['_rootComponents']; // Angular2 + // const rootComponents = this.applicationRef['components']; // Angular5 + if (rootComponents.length) { + return rootComponents[0]; + } + throw new Error('View Container not found! ngUpgrade needs to manually set this via setRootViewContainer.'); + } + + /** + * Gets the html element for a component ref. + * + * @param {ComponentRef} componentRef + * @returns {HTMLElement} + * + * @memberOf InjectionService + */ + private getComponentRootNode(componentRef: ComponentRef): HTMLElement { + return (componentRef.hostView as EmbeddedViewRef).rootNodes[0] as HTMLElement; + } + + /** + * Gets the root component container html element. + * + * @returns {HTMLElement} + * + * @memberOf InjectionService + */ + private getRootViewContainerNode(): HTMLElement { + return this.getComponentRootNode(this.getRootViewContainer()); + } + + /** + * Projects the inputs onto the component + * + * @param {ComponentRef} component + * @param {*} options + * @returns {ComponentRef} + * + * @memberOf InjectionService + */ + private projectComponentInputs(component: ComponentRef, options: any): ComponentRef { + if (options) { + const props = Object.getOwnPropertyNames(options); + for (const prop of props) { + component.instance[prop] = options[prop]; + } + } + + return component; + } + + public createComponentDynamically(componentClass: Type, options: any = {}, location: Element = this.getRootViewContainerNode()): ComponentRef { + const componentFactory = this.componentFactoryResolver.resolveComponentFactory(componentClass); + const componentRef = componentFactory.create(this.injector); + const componentRootNode = this.getComponentRootNode(componentRef); + + // project the options passed to the component instance + this.projectComponentInputs(componentRef, options); + this.applicationRef.attachView(componentRef.hostView); + + componentRef.onDestroy(() => { + this.applicationRef.detachView(componentRef.hostView); + }); + + location.appendChild(componentRootNode); + return componentRef; + } + + /** + * Inserts a component into an existing viewContainer + * @param componentType - type of component to create + * @param options - Inputs to project on new component + * @param vcRef - viewContainerRef in which to insert the newly created component + */ + public insertComponentDynamically(componentType: Type, options: any = {}, vcRef: ViewContainerRef): ComponentRef { + const factory = this.componentFactoryResolver.resolveComponentFactory(componentType); + const dynamicComponent = factory.create(vcRef.parentInjector); + this.projectComponentInputs(dynamicComponent, options); + vcRef.insert(dynamicComponent.hostView); + return dynamicComponent; + } +} diff --git a/src/react/Accordion.js b/src/react/Accordion.js new file mode 100644 index 0000000..3acdd24 --- /dev/null +++ b/src/react/Accordion.js @@ -0,0 +1,40 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import SVGIcon from './SVGIcon.js'; + +class Accordion extends React.Component { + constructor(props) { + super(props); + this.state = { + open: props.defaultExpanded + }; + } + render() { + const { children, title, className, dataTestId } = this.props; + const { open } = this.state; + return ( +
    +
    this.setState({ open: !open })} className='sdc-accordion-header'> + +
    {title}
    +
    +
    {children}
    +
    + ); + } +} + +Accordion.propTypes = { + title: PropTypes.string, + children: PropTypes.node, + expandByDefault: PropTypes.bool, + dataTestId: PropTypes.string +}; + +Accordion.defaultProps = { + title: '', + className: '', + defaultExpanded: false +}; + +export default Accordion; diff --git a/src/react/Button.js b/src/react/Button.js new file mode 100644 index 0000000..c628455 --- /dev/null +++ b/src/react/Button.js @@ -0,0 +1,37 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import SVGIcon from './SVGIcon.js'; + +const Button = ({btnType, size, className, iconName, onClick, disabled, children, ...other}) => ( + +); + +Button.propTypes = { + btnType: PropTypes.string, + size: PropTypes.oneOf(['', 'default', 'x-small', 'small', 'medium', 'large']), + className: PropTypes.string, + iconName: PropTypes.string, + onClick: PropTypes.func, + disabled: PropTypes.bool +}; + +Button.defaultProps = { + btnType: 'primary', + size: '', + className: '', + iconName: '', + disabled: false +}; + +export default Button; diff --git a/src/react/Checkbox.js b/src/react/Checkbox.js new file mode 100644 index 0000000..bef6945 --- /dev/null +++ b/src/react/Checkbox.js @@ -0,0 +1,45 @@ +import React from 'react'; + +class Checkbox extends React.Component { + + render() { + let {checked = false, disabled, value, label, inputRef, className, name} = this.props; + let dataTestId = this.props['data-test-id']; + + return ( +
    + +
    + ); + } + + onChange(e) { + let {onChange} = this.props; + if (onChange) { + onChange(e.target.checked); + } + } + + getChecked() { + return this.props.checked; + } + + getValue() { + return this.props.value; + } + +} + +export default Checkbox; diff --git a/src/react/Checklist.js b/src/react/Checklist.js new file mode 100644 index 0000000..1a42aee --- /dev/null +++ b/src/react/Checklist.js @@ -0,0 +1,43 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Checkbox from './Checkbox.js'; + +const Checklist = ({ items = [], className, onChange }) => ( +
    + {items.map((item, index) => { + return ( +
    + { + let obj = {}; + obj[item.value] = value; + onChange(obj); + }} + data-test-id={item.dataTestId} + /> +
    + ); + })} +
    +); + +Checklist.propTypes = { + items: PropTypes.arrayOf( + PropTypes.shape({ + label: PropTypes.string, + value: PropTypes.string, + checked: PropTypes.bool, + disabled: PropTypes.bool, + dataTestId: PropTypes.string + }) + ), + className: PropTypes.string, + onChange: PropTypes.func +}; + +export default Checklist; \ No newline at end of file diff --git a/src/react/Input.js b/src/react/Input.js new file mode 100644 index 0000000..5760637 --- /dev/null +++ b/src/react/Input.js @@ -0,0 +1,88 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import SVGIcon from './SVGIcon.js'; + +class Input extends React.Component { + + render() { + let {className, disabled, errorMessage, readOnly, label, name, value, type, placeholder, isRequired} = this.props; + let dataTestId = this.props['data-test-id']; + let inputClasses = `sdc-input__input ${errorMessage ? 'error' : ''} ${readOnly ? 'view-only' : ''}`; + let labelClasses = `sdc-input__label ${readOnly ? 'view-only' : ''} ${isRequired ? 'required' : ''}`; + + return ( +
    + + + this.onBlur(e)} + onKeyDown={(e) => this.onKeyDown(e)} + onChange={(e) => this.onChange(e)}/> + { errorMessage &&
    + +
    } +
    + ); + } + + onChange(e) { + let {onChange, readOnly, disabled} = this.props; + if (onChange && !readOnly && !disabled) { + onChange(e.target.value); + } + } + + onBlur(e) { + let {onBlur, readOnly} = this.props; + if (!readOnly && onBlur) { + onBlur(e); + } + } + + onKeyDown(e) { + let {onKeyDown, readOnly} = this.props; + if (!readOnly && onKeyDown) { + onKeyDown(e); + } + } + + getValue() { + return this.props.value; + } + +} +Input.propTypes = { + name: PropTypes.string, + value: PropTypes.string, + type: PropTypes.oneOf(['text', 'number']), + placeholder : PropTypes.string, + onChange: PropTypes.func, + onBlur: PropTypes.func, + onKeyDown: PropTypes.func, + errorMessage: PropTypes.string, + readOnly: PropTypes.bool, + isRequired: PropTypes.bool, + disabled: PropTypes.bool, + label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + className: PropTypes.string +}; + +Input.defaultProps = { + type: 'text', + readOnly: false, + isRequired: false, + disabled: false +}; +export default Input; diff --git a/src/react/Modal.js b/src/react/Modal.js new file mode 100644 index 0000000..ab2f7d7 --- /dev/null +++ b/src/react/Modal.js @@ -0,0 +1,55 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import Portal from './Portal.js'; +import Body from './ModalBody.js'; +import Header from './ModalHeader.js'; +import Footer from './ModalFooter.js'; +import Title from './ModalTitle.js'; + +export const modalSize = { + medium: 'md', + large: 'l', + extraLarge: 'xl', + small: 'sm', + extraSmall: 'xsm' +}; + + +class Modal extends React.Component { + + render() { + const {size, type, children, show} = this.props; + return ( + +
    { this.modalRef = el;}}> + {show &&
    +
    + {children} +
    +
    } + {show &&
    } +
    + + ); + } +} + +Modal.defaultProps = { + show: false, + size: 'medium', + type: 'info' +}; + +Modal.propTypes = { + show: PropTypes.bool, + size: PropTypes.string, + children: PropTypes.node, + type: PropTypes.string +}; + +Modal.Body = Body; +Modal.Header = Header; +Modal.Footer = Footer; +Modal.Title = Title; +export default Modal; \ No newline at end of file diff --git a/src/react/ModalBody.js b/src/react/ModalBody.js new file mode 100644 index 0000000..4fae0f6 --- /dev/null +++ b/src/react/ModalBody.js @@ -0,0 +1,19 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const ModalBody = ({children, className}) => ( +
    + {children} +
    +); + +ModalBody.propTypes = { + children: PropTypes.node, + className: PropTypes.string +}; + +ModalBody.defaultProps = { + className: '' +}; + +export default ModalBody; \ No newline at end of file diff --git a/src/react/ModalFooter.js b/src/react/ModalFooter.js new file mode 100644 index 0000000..607895d --- /dev/null +++ b/src/react/ModalFooter.js @@ -0,0 +1,36 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Button from './Button.js'; + +const Footer = ({onClose, closeButtonText, actionButtonText, actionButtonClick, withButtons, children}) => { + const closeBtnType = actionButtonClick ? 'secondary' : 'primary'; + return ( +
    + {children} + { + withButtons &&
    + {actionButtonClick && + + } + +
    + } +
    + ); +}; + +Footer.propTypes = { + onClose: PropTypes.func, + closeButtonText: PropTypes.string, + actionButtonText: PropTypes.string, + actionButtonClick: PropTypes.func, + withButtons: PropTypes.bool, + children: PropTypes.node +}; + +Footer.defaultProps = { + closeButtonText: 'Close', + withButtons: true +}; + +export default Footer; \ No newline at end of file diff --git a/src/react/ModalHeader.js b/src/react/ModalHeader.js new file mode 100644 index 0000000..c6be5ef --- /dev/null +++ b/src/react/ModalHeader.js @@ -0,0 +1,41 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import SVGIcon from './SVGIcon.js'; + +const iconMaper = { + error: 'error', + info: 'errorCircle', + alert: 'exclamationTriangleLine' +}; + +const headerTypes = { + error: 'sdc-error__header', + info: 'sdc-info__header', + alert: 'sdc-alert__header', + custom: 'sdc-custom__header' +} + + + +const Header = ({children, onClose, type}) => ( +
    + {type !== 'custom' + && + + + } + {children} + +
    +); + +Header.propTypes = { + children: PropTypes.node, + onClose: PropTypes.func +}; + +Header.defaultProps = { + type: 'info' +}; + +export default Header; \ No newline at end of file diff --git a/src/react/ModalTitle.js b/src/react/ModalTitle.js new file mode 100644 index 0000000..b48cc8a --- /dev/null +++ b/src/react/ModalTitle.js @@ -0,0 +1,19 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const Title = ({children, className}) => ( +
    + {children} +
    +); + +Title.PropTypes = { + children: PropTypes.node, + className: PropTypes.string +}; + +Title.defaultProps = { + className: '' +}; + +export default Title; \ No newline at end of file diff --git a/src/react/Panel.js b/src/react/Panel.js new file mode 100644 index 0000000..34d2e62 --- /dev/null +++ b/src/react/Panel.js @@ -0,0 +1,18 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const Panel = ({ className, children }) => ( +
    + {children} +
    +); + +Panel.propTypes = { + className: PropTypes.string, + children: PropTypes.node +}; + +Panel.defaultProps = { + className: '' +}; +export default Panel; \ No newline at end of file diff --git a/src/react/PopupMenu.js b/src/react/PopupMenu.js new file mode 100644 index 0000000..d2cd29a --- /dev/null +++ b/src/react/PopupMenu.js @@ -0,0 +1,39 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import PopupMenuItem from './PopupMenuItem'; + +class PopupMenu extends React.Component { + render() { + const {children = [], onMenuItemClick, position = {}, relative} = this.props; + const style = relative ? {left: position.x, top: position.y} : {}; + + return ( +
      + {React.Children.toArray(children).map((child, i) => React.cloneElement(child, + { + onClick: child.props.onClick || onMenuItemClick, + key: i + }))} +
    + ); + } +} + +PopupMenu.propTypes = { + relative: PropTypes.bool, + position: PropTypes.shape({ + x: PropTypes.number, + y: PropTypes.number + }), + onMenuItemClick: PropTypes.func +}; + +PopupMenu.defaultProps = { + relative: false +}; + +export const PopupMenuSeparator = () =>
  • ; + +PopupMenu.Separator = PopupMenuSeparator; +PopupMenu.Item = PopupMenuItem; +export default PopupMenu; diff --git a/src/react/PopupMenuItem.js b/src/react/PopupMenuItem.js new file mode 100644 index 0000000..98e3f49 --- /dev/null +++ b/src/react/PopupMenuItem.js @@ -0,0 +1,34 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +class PopupMenuItem extends React.Component { + render() { + const {itemId, value, onClick, selected, disabled} = this.props; + const additionalClasses = selected ? 'selected' : disabled ? 'disabled' : ''; + return ( +
  • { + event.stopPropagation(); + onClick && !disabled && onClick(itemId); + }}> + {value} +
  • + ); + } +} + +PopupMenuItem.propTypes = { + itemId: PropTypes.any, + value: PropTypes.any, + selected: PropTypes.bool, + onClick: PropTypes.func, + disabled: PropTypes.bool +}; + +PopupMenuItem.defaultProps = { + selected: false, + disabled: false +}; + +export default PopupMenuItem; diff --git a/src/react/Portal.js b/src/react/Portal.js new file mode 100644 index 0000000..90e0675 --- /dev/null +++ b/src/react/Portal.js @@ -0,0 +1,52 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ReactDOM from 'react-dom'; + +class Portal extends React.Component { + componentDidMount() { + this.renderPortal(); + } + + componentDidUpdate() { + this.renderPortal(); + } + + componentWillUnmount() { + if (this.defaultNode) { + document.body.removeChild(this.defaultNode); + } + this.defaultNode = null; + this.portal = null; + } + + renderPortal() { + if (!this.defaultNode) { + this.defaultNode = document.createElement('div'); + this.defaultNode.className = 'onap-sdc-portal'; + document.body.appendChild(this.defaultNode); + } + + let children = this.props.children; + if (typeof this.props.children.type === 'function') { + children = React.cloneElement(this.props.children); + } + /** + * Change this to ReactDOM.CreatePortal after upgrading to React 16 + */ + this.portal = ReactDOM.unstable_renderSubtreeIntoContainer( + this, + children, + this.defaultNode + ); + } + render() { + return null; + } + +} + +Portal.propTypes = { + children: PropTypes.node.isRequired +}; + +export default Portal; \ No newline at end of file diff --git a/src/react/Radio.js b/src/react/Radio.js new file mode 100644 index 0000000..483521a --- /dev/null +++ b/src/react/Radio.js @@ -0,0 +1,58 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +class Radio extends React.Component { + render() { + let {checked, disabled, value, label, className, inputRef, name} = this.props; + let dataTestId = this.props['data-test-id']; + return ( +
    + +
    + ); + } + + onChange(e) { + let {onChange} = this.props; + if (onChange) { + onChange(e.target.checked); + } + } + + getChecked() { + return this.props.checked; + } + + getValue() { + return this.props.value; + } +} + +Radio.propTypes = { + checked: PropTypes.bool, + value: PropTypes.any, + label: PropTypes.string, + className: PropTypes.string, + inputRef: PropTypes.func, + name: PropTypes.string, + disabled: PropTypes.bool +}; + +Radio.defaultProps = { + checked: false, + className: '' +}; + +export default Radio; diff --git a/src/react/RadioGroup.js b/src/react/RadioGroup.js new file mode 100644 index 0000000..59eaca7 --- /dev/null +++ b/src/react/RadioGroup.js @@ -0,0 +1,40 @@ +import React from 'react'; +import Radio from './Radio.js'; + +class RadioGroup extends React.Component { + constructor(props) { + super(props); + this.radios = {}; + } + + render() { + let {name, disabled, title, options, value, className} = this.props; + let dataTestId = this.props['data-test-id']; + return (
    + { title && } +
    + {options.map(option => { + let rName = name + '_' + option.value; + return ( {this.radios[rName] = radio;}} data-test-id={dataTestId + '_' + option.value} + key={rName} value={option.value} + label={option.label} checked={value === option.value} disabled={disabled} + name={name} onChange={() => this.onChange(rName)} /> + );})} +
    +
    ); + } + + onChange(rName) { + let {onChange} = this.props; + let val = this.radios[rName].getValue(); + if (onChange) { + onChange(val); + } + } + + getValue() { + return this.props.value; + } +} + +export default RadioGroup; diff --git a/src/react/SVGIcon.js b/src/react/SVGIcon.js new file mode 100644 index 0000000..8a5b1ae --- /dev/null +++ b/src/react/SVGIcon.js @@ -0,0 +1,47 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import iconMap from './utils/iconMap.js'; + +const SVGIcon = ({name, onClick, label, className, iconClassName, labelClassName, labelPosition, color, disabled, ...other}) => { + + let colorClass = (color !== '') ? '__' + color : ''; + let classes = `svg-icon-wrapper ${iconClassName} ${className} ${colorClass} ${onClick ? 'clickable' : ''} ${labelPosition}`; + let camelCasedName = name.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); }); + let IconComponent = iconMap[camelCasedName]; + if (!IconComponent) { + console.error('Icon by the name ' + camelCasedName + ' is missing.'); + } + + return ( +
    + { IconComponent && } + { !IconComponent && Missing Icon } + {label && {label}} +
    + ); + +}; + +SVGIcon.propTypes = { + name: PropTypes.string.isRequired, + onClick: PropTypes.func, + label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + labelPosition: PropTypes.string, + className: PropTypes.string, + iconClassName: PropTypes.string, + labelClassName: PropTypes.string, + color: PropTypes.string +}; + +SVGIcon.defaultProps = { + name: '', + label: '', + className: '', + iconClassName: '', + labelClassName: '', + labelPosition: 'bottom', + color: '' +}; + +export default SVGIcon; diff --git a/src/react/Tab.js b/src/react/Tab.js new file mode 100644 index 0000000..5aa0f16 --- /dev/null +++ b/src/react/Tab.js @@ -0,0 +1,20 @@ +import React from 'react'; + +class Tab extends React.Component { + render() { + const {activeTab, tabId, title, onClick, disabled, className = ''} = this.props; + const dataTestId = this.props['data-test-id']; + return ( +
  • + {title} +
  • + ); + } +} + +export default Tab; diff --git a/src/react/TabPane.js b/src/react/TabPane.js new file mode 100644 index 0000000..56a4bf0 --- /dev/null +++ b/src/react/TabPane.js @@ -0,0 +1,12 @@ +import React from 'react'; + +class TabPane extends React.Component { + render() { + const {children} = this.props; + return (
    + {children} +
    ); + } +} + +export default TabPane; diff --git a/src/react/Tabs.js b/src/react/Tabs.js new file mode 100644 index 0000000..c502038 --- /dev/null +++ b/src/react/Tabs.js @@ -0,0 +1,29 @@ +import React from 'react'; +import TabPane from './TabPane.js'; + +class Tabs extends React.Component { + render() { + const {type, children = [], activeTab, onTabClick, className} = this.props; + return ( +
    +
      + {children.map(child => React.cloneElement(child, + { + key: child.props.tabId, + onClick: () => onTabClick(child.props.tabId), + activeTab + }))} +
    + + {children.map(child => { + if (child.props.tabId === activeTab) { + return child.props.children; + } + })} + +
    + ); + } +} + +export default Tabs; diff --git a/src/react/Tile.js b/src/react/Tile.js new file mode 100644 index 0000000..f47f88d --- /dev/null +++ b/src/react/Tile.js @@ -0,0 +1,33 @@ +import React, {Children} from 'react'; +import PropTypes from 'prop-types'; +import TileInfo from './TileInfo.js'; +import TileFooter from './TileFooter.js'; +import SVGIcon from './SVGIcon.js'; + +const Tile = ({headerText, headerColor, iconName, iconColor, className, onClick, children, dataTestId}) => { + let childrenArr = Children.toArray(children); + return ( +
    +
    {headerText}
    +
    +
    + {iconName && } +
    + {childrenArr.find(e => e.type === TileInfo)} +
    + {childrenArr.find(e => e.type === TileFooter)} +
    + ); +}; + +Tile.propTypes = { + headerText: PropTypes.string, + headerColor: PropTypes.string, + iconName: PropTypes.string, + iconColor: PropTypes.string, + className: PropTypes.string, + onClick: PropTypes.func, + dataTestId: PropTypes.string +}; + +export default Tile; diff --git a/src/react/TileFooter.js b/src/react/TileFooter.js new file mode 100644 index 0000000..3a56908 --- /dev/null +++ b/src/react/TileFooter.js @@ -0,0 +1,10 @@ +import React, {Children} from 'react'; +import TileFooterCell from './TileFooterCell.js'; + +const TileFooter = ({children, align}) => ( +
    + {Children.toArray(children).filter(e => e.type === TileFooterCell)} +
    +); + +export default TileFooter; diff --git a/src/react/TileFooterCell.js b/src/react/TileFooterCell.js new file mode 100644 index 0000000..37e6416 --- /dev/null +++ b/src/react/TileFooterCell.js @@ -0,0 +1,7 @@ +import React from 'react'; + +const TileFooterCell = ({className, children, dataTestId}) => ( + {children} +); + +export default TileFooterCell; diff --git a/src/react/TileInfo.js b/src/react/TileInfo.js new file mode 100644 index 0000000..bda8e74 --- /dev/null +++ b/src/react/TileInfo.js @@ -0,0 +1,10 @@ +import React, {Children} from 'react'; +import TileInfoLine from './TileInfoLine.js'; + +const TileInfo = ({align, children}) => ( +
    + {Children.toArray(children).filter(e => e.type === TileInfoLine)} +
    +); + +export default TileInfo; diff --git a/src/react/TileInfoLine.js b/src/react/TileInfoLine.js new file mode 100644 index 0000000..5b0e2c9 --- /dev/null +++ b/src/react/TileInfoLine.js @@ -0,0 +1,7 @@ +import React from 'react'; + +const TileInfoLine = ({type, className, children, dataTestId}) => ( +
    {children}
    +); + +export default TileInfoLine; diff --git a/src/react/index.js b/src/react/index.js new file mode 100644 index 0000000..cbe0161 --- /dev/null +++ b/src/react/index.js @@ -0,0 +1,74 @@ +import Accordion from './Accordion.js'; +import Button from './Button.js'; +import Checkbox from './Checkbox.js'; +import Checklist from './Checklist.js'; +import Input from './Input.js'; +import Modal from './Modal.js'; +import ModalBody from './ModalBody.js'; +import ModalFooter from './ModalFooter.js'; +import ModalHeader from './ModalHeader.js'; +import ModalTitle from './ModalTitle.js'; +import Panel from './Panel.js'; +import PopupMenu from './PopupMenu.js'; +import Portal from './Portal.js'; +import Radio from './Radio.js'; +import RadioGroup from './RadioGroup.js'; +import SVGIcon from './SVGIcon.js'; +import Tab from './Tab.js'; +import Tabs from './Tabs.js'; +import Tile from './Tile.js'; +import TileInfo from './TileInfo.js'; +import TileInfoLine from './TileInfoLine.js'; +import TileFooter from './TileFooter.js'; +import TileFooterCell from './TileFooterCell.js'; + + +export { Accordion }; +export { Button }; +export { Checkbox }; +export { Checklist }; +export { Input }; +export { Modal }; +export { ModalBody }; +export { ModalFooter }; +export { ModalHeader }; +export { ModalTitle }; +export { Panel }; +export { PopupMenu }; +export { Portal }; +export { Radio }; +export { RadioGroup }; +export { SVGIcon }; +export { Tab }; +export { Tabs }; +export { Tile }; +export { TileInfo }; +export { TileInfoLine }; +export { TileFooter }; +export { TileFooterCell }; + +export default { + Accordion, + Button, + Checkbox, + Checklist, + Input, + Modal, + ModalBody, + ModalFooter, + ModalHeader, + ModalTitle, + Panel, + PopupMenu, + Portal, + Radio, + RadioGroup, + SVGIcon, + Tab, + Tabs, + Tile, + TileInfo, + TileInfoLine, + TileFooter, + TileFooterCell +}; diff --git a/src/style/scss/_common.scss b/src/style/scss/_common.scss new file mode 100644 index 0000000..7daac20 --- /dev/null +++ b/src/style/scss/_common.scss @@ -0,0 +1,7 @@ +@import "common/normalize"; +@import "common/variables"; +@import "common/mixins"; +@import "common/typography"; +@import "common/base"; +@import "common/icons"; +@import "common/animation"; diff --git a/src/style/scss/_components.scss b/src/style/scss/_components.scss new file mode 100644 index 0000000..3b0d28d --- /dev/null +++ b/src/style/scss/_components.scss @@ -0,0 +1,22 @@ +@import "../../../components/button/button"; +@import "../../../components/tile/tile"; +@import "../../../components/checkbox/checkbox"; +@import "../../../components/radio/radio"; +@import "../../../components/radioGroup/radioGroup"; +@import "../../../components/tabs/tabs"; +@import "../../../components/icon/icon"; +@import "../../../components/input/input"; +@import "../../../components/dropdown/dropdown"; +@import "../../../components/modal/modal"; +@import "../../../components/menu/menu"; +@import "../../../components/filter-bar/_filter-bar"; +@import "../../../components/search-bar/_search-bar"; +@import "../../../components/checklist/checklist"; +@import "../../../components/autocomplete/autocomplete"; +@import "../../../components/tooltip/tooltip"; +@import "../../../components/tag-cloud/_tag-cloud"; +@import "../../../components/notification/notification"; +@import "../../../components/notifications-container/notifications-container"; +@import "../../../components/accordion/accordion"; +@import "../../../components/panel/panel"; +@import "../../../components/validation/validation"; diff --git a/src/style/scss/angular/_svg_icon.scss b/src/style/scss/angular/_svg_icon.scss new file mode 100644 index 0000000..16be14b --- /dev/null +++ b/src/style/scss/angular/_svg_icon.scss @@ -0,0 +1,210 @@ +@mixin color-icon($primary-color) { + color: $primary-color; + fill: $primary-color; +} + +@mixin color-icon-hover($secondary-color) { + &.clickable { + &:not([disabled]):hover, &:active, &:focus { + @include color-icon($secondary-color); + } + } +} + +@mixin color-icon-label($primary-color) { + @include color-icon($primary-color); + + .svg-icon { + @include color-icon($primary-color); + } +} + +@mixin color-icon-label-hover($secondary-color) { + &.clickable { + &:not([disabled]):hover, &:active, &:focus { + @include color-icon-label($secondary-color); + } + } +} + +.svg-icon { + display: inline-flex; + width: 24px; + height: 24px; + + & > svg { + width: 100%; + height: 100%; + } + + &[disabled] { + opacity: 0.7; + } + + &.mode-primary { + @include color-icon($blue); + @include color-icon-hover($light-blue); + } + + &.mode-secondary { + @include color-icon($gray); + @include color-icon-hover($dark-gray); + } + + &.mode-success { + @include color-icon($green); + } + + &.mode-error { + @include color-icon($red); + } + + &.mode-warning { + @include color-icon($yellow); + } + + &.mode-info { + @include color-icon($text-black); + @include color-icon-hover($dark-blue); + } + + &.size-x_small { + width: 12px; + height: 12px; + } + + &.size-small { + width: 16px; + height: 16px; + } + + &.size-medium { + width: 24px; + height: 24px; + } + + &.size-large { + width: 36px; + height: 36px; + } + + &.size-x_large { + width: 48px; + height: 48px; + } +} + +.svg-icon-wrapper { + display: inline-flex; + justify-content: center; + align-items: center; + + &.svg-icon-label { + } + + &.svg-icon { + } + + &[disabled] { + opacity: 0.7; + } + + &.label-placement-bottom { + flex-direction: column; + .svg-icon-label { + margin-top: 0.25em; + } + } + + &.label-placement-right { + .svg-icon-label { + margin-left: 0.25em; + } + } + + &.label-placement-top { + flex-direction: column-reverse; + .svg-icon-label { + margin-bottom: 0.25em; + } + } + + &.label-placement-left { + flex-direction: row-reverse; + .svg-icon-label { + margin-right: 0.25em; + } + } + + &.mode-primary { + @include color-icon-label($blue); + @include color-icon-label-hover($light-blue); + } + + &.mode-secondary { + @include color-icon-label($gray); + @include color-icon-label-hover($dark-gray); + } + + &.mode-success { + @include color-icon-label($green); + } + + &.mode-error { + @include color-icon-label($red); + } + + &.mode-warning { + @include color-icon-label($yellow); + } + + &.mode-info { + @include color-icon-label($text-black); + @include color-icon-label-hover($dark-blue); + } + + &.size-x_small { + font-size: 8px; + line-height: 10px; + + .svg-icon { + @extend .svg-icon.size-x_small; + } + } + + &.size-small { + font-size: 12px; + line-height: 14px; + + .svg-icon { + @extend .svg-icon.size-small; + } + } + + &.size-medium { + font-size: 16px; + line-height: 20px; + + .svg-icon { + @extend .svg-icon.size-medium; + } + } + + &.size-large { + font-size: 24px; + line-height: 28px; + + .svg-icon { + @extend .svg-icon.size-large; + } + } + + &.size-x_large { + font-size: 34px; + line-height: 40px; + + .svg-icon { + @extend .svg-icon.size-x_large; + } + } +} diff --git a/src/style/scss/angular/_tooltip_custom_style.scss b/src/style/scss/angular/_tooltip_custom_style.scss new file mode 100644 index 0000000..886b1dc --- /dev/null +++ b/src/style/scss/angular/_tooltip_custom_style.scss @@ -0,0 +1,9 @@ +.sdc-custom-tooltip { + background-color: $dark-blue; + border-color: $dark-blue; + border-radius: 10px; + + &:after { + border-color: $dark-blue transparent transparent transparent; + } +} diff --git a/src/style/scss/common/_animation.scss b/src/style/scss/common/_animation.scss new file mode 100644 index 0000000..659bd3b --- /dev/null +++ b/src/style/scss/common/_animation.scss @@ -0,0 +1,149 @@ +/*********************************************************************************** + VERTICAL COLLAPSE-EXPEND TRANSITION ANIMATION PAIR. + + We use the 'transition-vertical-collapse' for the collapse/idle block element, + and the 'transition-vertical-expand' to expend that element. + + -important: The element that will be used for the animation should be + a block element, adn have a content or width and height settings for it to work. +*********************************************************************************/ + +/** +Enable to fold an expended block element +@param $offsetY - The top position from which the drop down should fold + */ +@mixin keyframes-expand-animation($name, $maxHeight, $boxShadow:0 0 12px 0px rgba(0,0,0,.3), $margin:0){ + @keyframes #{$name} { + 0% { + opacity: 0; + max-height: 0; + overflow: hidden; + box-shadow: 0 0 0px 0px rgba(0,0,0,.3); + margin:0; + } + 10% { + opacity: 1; + margin: $margin; + } + 50% { + box-shadow: $boxShadow; + } + 99%{ + max-height:$maxHeight; + + overflow: hidden; + } + 100%{ + opacity: 1; + max-height:$maxHeight; + overflow: auto; + } + } +} + +/** +Enable to expend a folded block element +@param $maxHeight - most of the animation is done over the max-height property + so we have to set the maximum height the expended element can expend to. + */ +@mixin keyframes-collapse-animation($name, $maxHeight, $boxShadow:0 0 12px 0px rgba(0,0,0,.3)){ + @keyframes #{$name} { + 0% { + opacity: 1; + max-height:$maxHeight; + box-shadow: $boxShadow; + overflow: hidden; + } + 40%{ + opacity: 1; + } + 99%{ + opacity: 0; + max-height: 0; + overflow: hidden; + box-shadow: 0 0 0px 0px rgba(0,0,0,.3); + } + 100%{ + opacity: 0; + max-height: 0; + overflow: auto; + } + } +} + +/******************************************************************************** + SIMPLE FADE-IN KEYFRAMES ANIMATION (Used in tooltip for example) + + we use 'mixin-keyframes-fade-in-vertically' to create css @keyframes rule that + we later can use with animation property inside our prefered css rules: + .our_class { + ... + animation: keyframes-fade-in-vertically 1s ease-out; + ... + } +*********************************************************************************/ +@mixin mixin-keyframes-fade-in-vertically($fromRelativeHeight, $keyframesName:keyframes-fade-in-vertically){ + @keyframes #{$keyframesName} { + from { + transform: translateY($fromRelativeHeight); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } + } +} + +/******************************************************************************** + SIMPLE FADE-OUT KEYFRAMES ANIMATION (Opposite of fade-in mixin above) +*********************************************************************************/ +@mixin mixin-keyframes-fade-out-vertically($toRelativeHeight, $keyframesName:keyframes-fade-out-vertically){ + @keyframes #{$keyframesName} { + from { + transform: translateY(0); + opacity: 1; + } + to { + transform: translateY($toRelativeHeight); + opacity: 0; + } + } +} + + + +/******************************************************************************** + RIPPLE ANIMATION (Used for ripple-click directive) +*********************************************************************************/ +@keyframes ripple-animation { + from { + transform: scale(0,0); + opacity: 1; + } + to { + transform: scale(2,2); + opacity: 0; + } +} + +.sdc-ripple-click__animated { + position:relative; +} +.sdc-ripple-click__animated::before{ + display: inline-block; + position:absolute; + top: 0; + left: 0; + content: ''; + animation: ripple-animation .3s ease-out; + background-color: $blue; + width: 14px; + height: 14px; + border-radius: 50%; + pointer-events: none; + opacity: 0; +} + + + diff --git a/src/style/scss/common/_icons.scss b/src/style/scss/common/_icons.scss new file mode 100644 index 0000000..00f425d --- /dev/null +++ b/src/style/scss/common/_icons.scss @@ -0,0 +1,19 @@ +.sdc-icon { + display: inline-block; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + width: 16px; + height: 16px; +} + +.sdc-icon-locked {background-image: url("data:image/svg+xml;utf8, "); background-repeat: no-repeat;} +.sdc-icon-plus {background-image: url("data:image/svg+xml;utf8,"); background-repeat: no-repeat;} +.sdc-icon-unlocked {background-image: url("data:image/svg+xml;utf8, "); background-repeat: no-repeat;} +.sdc-icon-vendor {background-image: url("data:image/svg+xml;utf8,vendor"); background-repeat: no-repeat;} +.sdc-icon-vlm {background-image: url("data:image/svg+xml;utf8,vlm_new_icon"); background-repeat: no-repeat;} +.sdc-icon-vsp {background-image: url("data:image/svg+xml;utf8,vsp_new_icon"); background-repeat: no-repeat;} + +.sdc-icon-transform{ + transform: rotate(180deg); +} diff --git a/src/style/scss/common/_normalize.scss b/src/style/scss/common/_normalize.scss new file mode 100644 index 0000000..9375ee9 --- /dev/null +++ b/src/style/scss/common/_normalize.scss @@ -0,0 +1,578 @@ +/* ========================================================================== + Normalize.scss settings + ========================================================================== */ +/** + * Includes legacy browser support IE6/7 + * + * Set to false if you want to drop support for IE6 and IE7 + */ + +$legacy_browser_support: false !default; + +/* Base + ========================================================================== */ + +/** + * 1. Set default font family to sans-serif. + * 2. Prevent iOS and IE text size adjust after device orientation change, + * without disabling user zoom. + * 3. Corrects text resizing oddly in IE 6/7 when body `font-size` is set using + * `em` units. + */ + +html { + font-family: sans-serif; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ + @if $legacy_browser_support { + *font-size: 100%; /* 3 */ + } +} + +/** + * Remove default margin. + */ + +body { + margin: 0; +} + +/* HTML5 display definitions + ========================================================================== */ + +/** + * Correct `block` display not defined for any HTML5 element in IE 8/9. + * Correct `block` display not defined for `details` or `summary` in IE 10/11 + * and Firefox. + * Correct `block` display not defined for `main` in IE 11. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} + +/** + * 1. Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. + * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. + */ + +audio, +canvas, +progress, +video { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ + @if $legacy_browser_support { + *display: inline; + *zoom: 1; + } +} + +/** + * Prevents modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Address `[hidden]` styling not present in IE 8/9/10. + * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22. + */ + +[hidden], +template { + display: none; +} + +/* Links + ========================================================================== */ + +/** + * Remove the gray background color from active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * Improve readability of focused elements when they are also in an + * active/hover state. + */ + +a { + &:active, &:hover { + outline: 0; + }; +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Address styling not present in IE 8/9/10/11, Safari, and Chrome. + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/** + * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. + */ + +b, +strong { + font-weight: bold; +} + +@if $legacy_browser_support { + blockquote { + margin: 1em 40px; + } +} + +/** + * Address styling not present in Safari and Chrome. + */ + +dfn { + font-style: italic; +} + +/** + * Address variable `h1` font-size and margin within `section` and `article` + * contexts in Firefox 4+, Safari, and Chrome. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +@if $legacy_browser_support { + h2 { + font-size: 1.5em; + margin: 0.83em 0; + } + + h3 { + font-size: 1.17em; + margin: 1em 0; + } + + h4 { + font-size: 1em; + margin: 1.33em 0; + } + + h5 { + font-size: 0.83em; + margin: 1.67em 0; + } + + h6 { + font-size: 0.67em; + margin: 2.33em 0; + } +} + +/** + * Addresses styling not present in IE 8/9. + */ + +mark { + background: #ff0; + color: #000; +} + +@if $legacy_browser_support { + + /** + * Addresses margins set differently in IE 6/7. + */ + + p, + pre { + *margin: 1em 0; + } + + /* + * Addresses CSS quotes not supported in IE 6/7. + */ + + q { + *quotes: none; + } + + /* + * Addresses `quotes` property not supported in Safari 4. + */ + + q:before, + q:after { + content: ''; + content: none; + } +} + +/** + * Address inconsistent and variable font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` affecting `line-height` in all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +@if $legacy_browser_support { + + /* ========================================================================== + Lists + ========================================================================== */ + + /* + * Addresses margins set differently in IE 6/7. + */ + + dl, + menu, + ol, + ul { + *margin: 1em 0; + } + + dd { + *margin: 0 0 0 40px; + } + + /* + * Addresses paddings set differently in IE 6/7. + */ + + menu, + ol, + ul { + *padding: 0 0 0 40px; + } + + /* + * Corrects list images handled incorrectly in IE 7. + */ + + nav ul, + nav ol { + *list-style: none; + *list-style-image: none; + } + +} + +/* Embedded content + ========================================================================== */ + +/** + * 1. Remove border when inside `a` element in IE 8/9/10. + * 2. Improves image quality when scaled in IE 7. + */ + +img { + border: 0; + @if $legacy_browser_support { + *-ms-interpolation-mode: bicubic; /* 2 */ + } +} + +/** + * Correct overflow not hidden in IE 9/10/11. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Grouping content + ========================================================================== */ + +/** + * Address margin not present in IE 8/9 and Safari. + */ + +figure { + margin: 1em 40px; +} + +/** + * Address differences between Firefox and other browsers. + */ + +hr { + box-sizing: content-box; + height: 0; +} + +/** + * Contain overflow in all browsers. + */ + +pre { + overflow: auto; +} + +/** + * Address odd `em`-unit font size rendering in all browsers. + * Correct font family set oddly in IE 6, Safari 4/5, and Chrome. + */ + +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + @if $legacy_browser_support { + _font-family: 'courier new', monospace; + } + font-size: 1em; +} + +/* Forms + ========================================================================== */ + +/** + * Known limitation: by default, Chrome and Safari on OS X allow very limited + * styling of `select`, unless a `border` property is set. + */ + +/** + * 1. Correct color not being inherited. + * Known issue: affects color of disabled elements. + * 2. Correct font properties not being inherited. + * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. + * 4. Improves appearance and consistency in all browsers. + */ + +button, +input, +optgroup, +select, +textarea { + color: inherit; /* 1 */ + font: inherit; /* 2 */ + margin: 0; /* 3 */ + @if $legacy_browser_support { + vertical-align: baseline; /* 3 */ + *vertical-align: middle; /* 3 */ + } +} + +/** + * Address `overflow` set to `hidden` in IE 8/9/10/11. + */ + +button { + overflow: visible; +} + +/** + * Address inconsistent `text-transform` inheritance for `button` and `select`. + * All other form control elements do not inherit `text-transform` values. + * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. + * Correct `select` style inheritance in Firefox. + */ + +button, +select { + text-transform: none; +} + +/** + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Correct inability to style clickable `input` types in iOS. + * 3. Improve usability and consistency of cursor style between image-type + * `input` and others. + * 4. Removes inner spacing in IE 7 without affecting normal text inputs. + * Known issue: inner spacing remains in IE 6. + */ + +button, +html input[type="button"], /* 1 */ +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* 2 */ + cursor: pointer; /* 3 */ + @if $legacy_browser_support { + *overflow: visible; /* 4 */ + } +} + +/** + * Re-set default cursor for disabled elements. + */ + +button[disabled], +html input[disabled] { + cursor: default; +} + +/** + * Remove inner padding and border in Firefox 4+. + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/** + * Address Firefox 4+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ + +input { + line-height: normal; +} + +/** + * 1. Address box sizing set to `content-box` in IE 8/9/10. + * 2. Remove excess padding in IE 8/9/10. + * Known issue: excess padding remains in IE 6. + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ + @if $legacy_browser_support { + *height: 13px; /* 3 */ + *width: 13px; /* 3 */ + } +} + +/** + * Fix the cursor style for Chrome's increment/decrement buttons. For certain + * `font-size` values of the `input`, it causes the cursor style of the + * decrement button to change from `default` to `text`. + */ + +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Address `appearance` set to `searchfield` in Safari and Chrome. + * 2. Address `box-sizing` set to `border-box` in Safari and Chrome. + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ + box-sizing: content-box; /* 2 */ +} + +/** + * Remove inner padding and search cancel button in Safari and Chrome on OS X. + * Safari (but not Chrome) clips the cancel button when the search input has + * padding (and `textfield` appearance). + */ + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * Define consistent border, margin, and padding. + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/** + * 1. Correct `color` not being inherited in IE 8/9/10/11. + * 2. Remove padding so people aren't caught out if they zero out fieldsets. + * 3. Corrects text not wrapping in Firefox 3. + * 4. Corrects alignment displayed oddly in IE 6/7. + */ + +legend { + border: 0; /* 1 */ + padding: 0; /* 2 */ + @if $legacy_browser_support { + white-space: normal; /* 3 */ + *margin-left: -7px; /* 4 */ + } +} + +/** + * Remove default vertical scrollbar in IE 8/9/10/11. + */ + +textarea { + overflow: auto; +} + +/** + * Don't inherit the `font-weight` (applied by a rule above). + * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. + */ + +optgroup { + font-weight: bold; +} + +/* Tables + ========================================================================== */ + +/** + * Remove most spacing between table cells. + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} + +td, +th { + padding: 0; +} diff --git a/src/style/scss/common/_typography.scss b/src/style/scss/common/_typography.scss new file mode 100644 index 0000000..6fd59cc --- /dev/null +++ b/src/style/scss/common/_typography.scss @@ -0,0 +1,96 @@ +$heading-font-1: 28px; +$heading-font-2: 24px; +$heading-font-3: 20px; +$heading-font-4: 16px; +$heading-font-5: 14px; + +$body-font-1: 14px; +$body-font-2: 13px; +$body-font-3: 12px; +$body-font-4: 10px; + +@mixin base-font-regular() { + font-family: OpenSans-Regular, Arial, sans-serif; + font-style: normal; + font-weight: 400; +} + +@mixin base-font-italic(){ + font-family: OpenSans-Italic, OpenSans-Regular, Arial, sans-serif; + font-style: normal; + font-weight: 400; +} + +@mixin base-font-semibold() { + font-family: OpenSans-Semibold, Arial, sans-serif; + font-style: normal; + font-weight: 400; +} + +@mixin font-error() { + color: $red; +} + +@mixin heading-1() { + @include base-font-regular; + font-size: $heading-font-1; +} + +@mixin heading-2() { + @include base-font-regular; + font-size: $heading-font-2; +} + +@mixin heading-3 { + @include base-font-regular; + font-size: $heading-font-3; +} + +@mixin heading-4 { + @include base-font-regular; + font-size: $heading-font-4; +} + +@mixin heading-4-emphasis { + @include base-font-semibold; + font-size: $heading-font-4; +} + +@mixin heading-5 { + @include base-font-semibold; + font-size: $heading-font-5; +} + +@mixin body-1 { + @include base-font-regular; + font-size: $body-font-1; +} + +@mixin body-1-italic { + @include base-font-italic; + font-size: $body-font-1; +} + +@mixin body-2 { + @include base-font-regular; + font-size: $body-font-2; +} + +@mixin body-2-emphasis { + @include base-font-semibold; + font-size: $body-font-2; +} + +@mixin body-3 { + @include base-font-regular; + font-size: $body-font-3; +} +@mixin body-3-emphasis { + @include base-font-semibold; + font-size: $body-font-3; +} + +@mixin body-4 { + @include base-font-regular; + font-size: $body-font-4; +} \ No newline at end of file diff --git a/src/style/scss/common/base.scss b/src/style/scss/common/base.scss new file mode 100644 index 0000000..02baf81 --- /dev/null +++ b/src/style/scss/common/base.scss @@ -0,0 +1,96 @@ +html { + font-size: 100%; + height: 100%; +} + +body { + /* scrollbar styling for Internet Explorer */ + scrollbar-face-color: $light-gray; + scrollbar-track-color: $white; + scrollbar-shadow-color:$white; + scrollbar-arrow-color: $gray; + + height: 100%; + @extend %noselect; +} + +/* scrollbar styling for Google Chrome | Safari | Opera */ +::-webkit-scrollbar { + width: 11px; + height: 8px; +} + +::-webkit-scrollbar-track { + background-color: $white; + border: 1px solid $light-gray; + border-top:none; + border-bottom:none; +} + +::-webkit-scrollbar-thumb { + border-radius: 6px; + background-color: $gray; + border: 2px solid rgba(0,0,0,0); + background-clip: padding-box; + + &:hover { + border-width:1px 0px 1px 1px; + } +} + +/* Mozilla Firefox currently doesn't support scrollbar styling */ + +ul { + list-style: none; +} + +h1, h2, h3, h4, h5, h6, ul { + margin: 0; + padding: 0; +} + +input[type='text'] { + padding: 4px; + width: 100%; +} + +input[type="checkbox"] { + width: auto; +} + +input, select, button { + @include body-1; + box-sizing: border-box; +} + +fieldset { + border: none; +} + +fieldset { + label { + display: inline-block; + } +} + +.nav-tabs > li > a:focus, +.btn:focus, +.btn:active:focus, +.btn.active:focus { + outline: none; +} + +.error-message{ + color: $red; + @include body-3; + margin-top: 3px; + &:before{ + content: ""; + display: inline-block; + width: 14px; + height: 14px; + margin-right: 6px; + //not correct icon + background: no-repeat url('data:image/svg+xml;utf8,'); + } +} diff --git a/src/style/scss/common/mixins.scss b/src/style/scss/common/mixins.scss new file mode 100644 index 0000000..c4cb733 --- /dev/null +++ b/src/style/scss/common/mixins.scss @@ -0,0 +1,337 @@ +/* Colors */ +.sdc-bc-white { background-color: $white; } +.sdc-bc-blue { background-color: $blue; } +.sdc-bc-light-blue { background-color: $light-blue; } +.sdc-bc-lighter-blue { background-color: $lighter-blue; } +.sdc-bc-blue-disabled { background-color: $blue-disabled; } +.sdc-bc-dark-blue { background-color: $dark-blue; } +.sdc-bc-black { background-color: $black; } +.sdc-bc-rich-black { background-color: $rich-black; } +.sdc-bc-text-black { background-color: $text-black; } +.sdc-bc-dark-gray { background-color: $dark-gray; } +.sdc-bc-gray { background-color: $gray; } +.sdc-bc-light-gray { background-color: $light-gray; } +.sdc-bc-silver { background-color: $silver; } +.sdc-bc-light-silver { background-color: $light-silver; } +.sdc-bc-green { background-color: $green; } +.sdc-bc-red { background-color: $red; } +.sdc-bc-disabled-red { background-color: $disabled-red; } +.sdc-bc-light-red { background-color: $light-red; } +.sdc-bc-yellow { background-color: $yellow; } +.sdc-bc-dark-purple { background-color: $dark-purple; } +.sdc-bc-purple { background-color: $purple; } +.sdc-bc-light-purple { background-color: $light-purple; } +.sdc-bc-lighter-silver { background-color: $lighter-silver; } +/* Prefix */ +$box-sizing-prefix: webkit moz spec; +$border-radius-prefix: webkit spec; +$box-shadow-radius-prefix: webkit moz spec; +$text-shadow-radius-prefix: spec; +$text-shadow-prefix: spec; +$box-shadow-prefix: all; +$linear-gradient-prefix: all; +$transition-prefix: webkit moz o spec; +$flex-prefix: webkit spec; +$browserPrefixes: webkit moz o ms; + +@mixin prefix($property, $value, $prefixeslist: 'all') { + @if $prefixeslist == all { + -webkit-#{$property}: $value; + -moz-#{$property}: $value; + -ms-#{$property}: $value; + -o-#{$property}: $value; + #{$property}: $value; + } @else { + @each $prefix in $prefixeslist { + @if $prefix == webkit { + -webkit-#{$property}: $value; + } @else if $prefix == moz { + -moz-#{$property}: $value; + } @else if $prefix == ms { + -ms-#{$property}: $value; + } @else if $prefix == o { + -o-#{$property}: $value; + } @else if $prefix == spec { + #{$property}: $value; + } @else { + @warn "No such prefix: #{$prefix}"; + } + } + } +} + +/* Value Prefix*/ +@mixin value-suffix-with-range($property, $valuesuffix, $from, $to, $prefixeslist) { + + @if $prefixeslist == all { + #{property} : -webkit-#{$valuesuffix}($from, $to); + #{property} : -moz-#{$valuesuffix}($from, $to); + #{property} : -o-#{$valuesuffix}($from, $to); + #{property} : -ms-#{$valuesuffix}($from, $to); + + } @else { + @each $prefix in $prefixeslist { + @if $prefix == webkit { + #{property} : -webkit-#{$valuesuffix}($from, $to); + } @else if $prefix == moz { + #{property} : -moz-#{$valuesuffix}($from, $to); + } @else if $prefix == ms { + #{property} : -ms-#{$valuesuffix}($from, $to); + } @else if $prefix == o { + #{property} : -o-#{$valuesuffix}($from, $to); + } @else { + @warn "No such prefix: #{$prefix}"; + } + } + } +} + +/* Box sizing */ +@mixin box-sizing($value: border-box) { + @include prefix(box-sizing, $value, $box-sizing-prefix); +} + +/* Borders & Shadows */ +@mixin box-shadow($value) { + @include prefix(box-shadow, $value, $box-shadow-radius-prefix); +} + +@mixin text-shadow($value) { + @include prefix(text-shadow, $value, $text-shadow-radius-prefix); +} + +@mixin border-radius($value, $positions: all) { + @if ($positions == all) { + @include prefix(border-radius, $value, $border-radius-prefix); + } @else { + @each $position in $positions { + @include prefix(border-#{$position}-radius, $value, $border-radius-prefix); + } + } + +} + +@mixin transition($value) { + @include prefix(transition, $value, $transition-prefix); +} + +/* Opacity */ +@mixin opacity($alpha) { + $ie-opacity: round($alpha * 100); + opacity: $alpha; + filter: unquote("alpha(opacity = #{$ie-opacity})"); +} + +/* Ellipsis */ +@mixin ellipsis($width: 100%, $display: inline-block, $max-width: none) { + overflow: hidden; + text-overflow: ellipsis; + width: $width; + white-space: nowrap; + display: $display; + max-width: $max-width; +} + +@mixin multiline-ellipsis($lineHeight: 1.3em, $lineCount: 2, $bgColor: $white){ + overflow: hidden; + position: relative; + line-height: $lineHeight; + max-height: $lineHeight * $lineCount; + text-align: justify; + // margin-right: -1em; + padding-right: 1em; + &:before { + content: '...'; + position: absolute; + right: 3px; + bottom: 0; + } + &:after { + content: ''; + position: absolute; + right: 0; + width: 1em; + height: 1em; + margin-top: 0.2em; + background: $bgColor; + } +} + +@mixin gradient($from, $to) { + /* fallback/image non-cover color */ + background-color: $from; + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from($from), to($to)); + @include value-suffix-with-range(background-color, linear-gradient, $from, $to, $linear-gradient-prefix); +} + +/* Vertical placement of multuple lines of text */ +@mixin vertical-text($height) { + position: absolute; + top: 50%; + margin-top: -$height/2; +} + +@mixin text-vertical-align($align: middle) { + display: table; + width: 100%; + + & > * { + vertical-align: $align; + display: table-cell; + } +} + +@mixin center-element($width) { + width: $width; + margin-left: auto; + margin-right: auto; +} + +@mixin center-content($width) { + & > * { + @include center-element($width); + } +} + +/* transform-rotate */ +// @mixin +// Defines a 2D rotation, the angle is specified in the parameter +// @param +// $deg - angle in degrees +@mixin transform-rotate($deg) { + transform: rotate($deg + deg); /* IE10 and Mozilla */ + -ms-transform: rotate($deg + deg); /* IE 9 */ + -webkit-transform: rotate($deg + deg); /* Safari and Chrome */ +} + +/* transform-translate */ +// @mixin +// Defines a 2D rotation, the angle is specified in the parameter +// @param +// $deg - angle in degrees +@mixin transform-translate($x, $y) { + transform: translate($x, $y); /* IE10 and Mozilla */ + -ms-transform: translate($x, $y); /* IE 9 */ + -webkit-transform: translate($x, $y); /* Safari and Chrome */ +} + +/* transform-scale */ +// @mixin +// Defines a 2D scale transformation, changing the elements width and height +// @param +// $width - width +// @param +// $height - height +@mixin transform-scale($width, $height) { + transform: scale($width, $height); /* IE10 and Mozilla */ + -ms-transform: scale($width, $height); /* IE 9 */ + -webkit-transform: scale($width, $height); /* Safari and Chrome */ +} + +@mixin scrollable() { + ::-webkit-scrollbar { + width: 8px; + } +} + +@mixin create-circle($size, $bgcolor, $content) { + border-radius: 50%; + width: $size; + height: $size; + background: $bgcolor; + border: 3px solid $bgcolor; + &:after { + content: $content; + position: relative; + left: 9px; + top: 9px; + @include base-font-semibold; + font-size: $body-font-1; + } +} + +/**/ +@mixin keyframe-animation($animationType, $properties, $fromValue, $toValue) { + + @keyframes #{$animationType} { + from { + $startIndex: 1; + @each $property in $properties { + #{$property}: nth($fromValue, $startIndex); + $startIndex: $startIndex + 1; + } + } + to { + $startIndex: 1; + @each $property in $properties { + #{$property}: nth($toValue, $startIndex); + $startIndex: $startIndex + 1; + } + } + } + @-moz-keyframes #{$animationType}{ + /* Firefox */ + from { + $startIndex: 1; + @each $property in $properties { + #{$property}: nth($fromValue, $startIndex); + $startIndex: $startIndex + 1; + } + } + to { + $startIndex: 1; + @each $property in $properties { + #{$property}: nth($toValue, $startIndex); + $startIndex: $startIndex + 1; + } + } + } + @-webkit-keyframes #{$animationType} { + /* Safari and Chrome */ + from { + $startIndex: 1; + @each $property in $properties { + #{$property}: nth($fromValue, $startIndex); + $startIndex: $startIndex + 1; + } + } + to { + $startIndex: 1; + @each $property in $properties { + #{$property}: nth($toValue, $startIndex); + $startIndex: $startIndex + 1; + } + } + } + @-o-keyframes #{$animationType} { + /* Opera */ + from { + $startIndex: 1; + @each $property in $properties { + #{$property}: nth($fromValue, $startIndex); + $startIndex: $startIndex + 1; + } + } + to { + $startIndex: 1; + @each $property in $properties { + #{$property}: nth($toValue, $startIndex); + $startIndex: $startIndex + 1; + } + } + } +} + + +/**/ +@mixin border-shadow($xShadow: 0.545px, $yShadow: 0.839px, $blur: 4px, $spread: 0, $color: $light-gray, $opacity: 0.2) { + @include box-shadow($xShadow $yShadow $blur $spread rgba($color, $opacity)); +} + +%noselect { + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} diff --git a/src/style/scss/common/variables.scss b/src/style/scss/common/variables.scss new file mode 100644 index 0000000..38eded4 --- /dev/null +++ b/src/style/scss/common/variables.scss @@ -0,0 +1,35 @@ +// Colors +$black: #000000; +$rich-black: #323943; +$text-black: #191919; +$blue: #009fdb; +$dark-blue: #0568ae; +$light-blue: #1eb9f3; +$lighter-blue: #e6f6fb; +$blue-disabled: #9dd9ef; +$red: #cf2a2a; +$light-red:#ed4141; +$disabled-red:#f4adad; +$purple: #9063cd; +$dark-purple: #702f8a; +$yellow: #ffb81c; +$green: #4ca90c; +$gray: #959595; +$dark-gray: #5a5a5a; +$light-gray: #d2d2d2; +$light-silver: #f2f2f2; +$silver: #eaeaea; + + +$light-purple: #caa2dd; +$lighter-silver: #f8f8f8; +$white: #ffffff; + +$scroll-bar-color: $text-black; + +// Button Sizes +$btn-extra-small: 90px; +$btn-small: 110px; +$btn-medium: 140px; +$btn-large: 180px; +$btn-default: auto; diff --git a/src/style/scss/style.scss b/src/style/scss/style.scss new file mode 100644 index 0000000..5512776 --- /dev/null +++ b/src/style/scss/style.scss @@ -0,0 +1,6 @@ +@import "common"; +@import "components"; + +// for angular +@import "angular/svg_icon"; +@import "angular/tooltip_custom_style"; diff --git a/src/style/scss/themes/1802/_components.scss b/src/style/scss/themes/1802/_components.scss new file mode 100644 index 0000000..6800005 --- /dev/null +++ b/src/style/scss/themes/1802/_components.scss @@ -0,0 +1,23 @@ +/* Deafult theme */ +@import "../../../../../components/tile/tile"; +@import "../../../../../components/checkbox/checkbox"; +@import "../../../../../components/radio/radio"; +@import "../../../../../components/radioGroup/radioGroup"; +@import "../../../../../components/icon/icon"; +@import "../../../../../components/input/input"; +@import "../../../../../components/dropdown/dropdown"; +@import "../../../../../components/menu/menu"; +@import "../../../../../components/filter-bar/_filter-bar"; +@import "../../../../../components/search-bar/_search-bar"; +@import "../../../../../components/checklist/checklist"; +@import "../../../../../components/autocomplete/autocomplete"; +@import "../../../../../components/tooltip/tooltip"; +@import "../../../../../components/tag-cloud/_tag-cloud"; +@import "../../../../../components/notification/notification"; +@import "../../../../../components/notifications-container/notifications-container"; +@import "../../../../../components/validation/validation"; + +/* 1802 theme */ +@import "button"; +@import "modal"; +@import "tabs"; diff --git a/src/style/scss/themes/1802/button.scss b/src/style/scss/themes/1802/button.scss new file mode 100644 index 0000000..05d91d5 --- /dev/null +++ b/src/style/scss/themes/1802/button.scss @@ -0,0 +1,148 @@ +.sdc-button { + @include box-sizing; + display: inline-block; + + outline: none; + border-radius: 2px; + padding: 0 16px; + + height: 32px; + line-height: 32px; + width: 120px; + min-width: 90px; + + cursor: pointer; + text-align: center; + @include body-1; + &:disabled { + cursor: default; + } + + // Primary button + &.sdc-button__primary { + border: none; + background-color: $blue; + color: $white; + + &:not(:disabled) { + &:hover, &:active { + background-color: $light-blue; + } + &:focus:not(:active) { + border: 0.5px solid $white; + background-color: $light-blue; + box-shadow: 0px 0px 0px 1px $light-blue; + } + } + + &:disabled{ + background: $blue-disabled; + } + } + + // Secondary button + &.sdc-button__secondary { + border: 1px solid $light-gray; + background-color: transparent; + color: $text-black; + + &:not(:disabled) { + &:hover, &:active { + background-color: transparent; + color:$text-black; + border: 1px solid $gray; + } + &:focus:not(:active) { + color: $text-black; + box-shadow: inset 0px 0px 0px 0px $light-gray, 0px 0px 0px 1px $gray; + } + } + + &:disabled { + color: $blue-disabled; + border-color: $blue-disabled; + } + } + + // Link button + &.sdc-button__link { + background-color: transparent; + color: $blue; + fill: $blue; + border: none; + + &:not(:disabled) { + &:hover, &:active { + color: $light-blue; + } + &:focus:not(:active) { + border: 1px solid $dark-blue; + color: $light-blue; + } + } + + &:disabled{ + color: $blue-disabled; + } + } + + + // alert button + &.sdc-button__alert { + border: none; + background-color: $red; + color: $white; + + &:not(:disabled) { + &:hover, &:active { + background-color: $light-red; + } + &:focus:not(:active) { + border: 0.5px solid $white; + background-color: $light-red; + box-shadow: 0px 0px 0px 1px $light-red; + } + } + + &:disabled{ + background: $disabled-red; + } + } + + + /*** Sizes ***/ + &.btn-large{ + width: $btn-large; + } + + &.btn-medium{ + width: $btn-medium; + } + + &.btn-small{ + width: $btn-small; + } + + &.btn-x-small{ + width: $btn-extra-small; + } + + &.btn-default{ + width: $btn-default; + } + + /*** Buttons with icons ***/ + .sdc-icon-right{ + margin-left: 15px; + } + + .sdc-icon-left{ + margin-right: 15px; + } + + svg { + display: inline-block; + vertical-align: middle; + } +} + diff --git a/src/style/scss/themes/1802/modal.scss b/src/style/scss/themes/1802/modal.scss new file mode 100644 index 0000000..de99d52 --- /dev/null +++ b/src/style/scss/themes/1802/modal.scss @@ -0,0 +1,193 @@ + +.sdc-modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + + overflow: auto; + margin: auto; + display: flex; + align-items: center; + z-index: 1001; + + + svg path { + fill: inherit; + } + + .sdc-modal__wrapper { + @include body-1; + background: $white; + width: 100%; + + @include box-shadow(0 0 4px 0 rgba(0,0,0,0.50)); + color: $text-black; + display: flex; + flex-direction: column; + &.sdc-modal-type-info { + border-top: solid 6px $blue; + .sdc-modal__svg-use { + fill: $blue; + } + .svg-icon { + &.__errorCircle { + width: 30px; + height: 30px; + } + } + } + &.sdc-modal-type-alert { + border-top: solid 6px $yellow; + .sdc-modal__svg-use { + fill: $yellow; + } + .svg-icon { + &.__exclamationTriangleLine { + width: 30px; + height: 30px; + } + } + } + &.sdc-modal-type-error { + border-top: solid 6px $red; + .sdc-modal__svg-use { + fill: $red; + } + .svg-icon { + &.__error { + width: 30px; + height: 30px; + } + } + } + &.sdc-modal-type-custom { + padding: 0 30px; + border-radius: 4px; + + .sdc-custom__header { + @include box-sizing; + color: $dark-gray; + height: 50px; + border-bottom: solid 3px $blue; + padding: 0; + + .title { + @include heading-3; + color: $dark-gray; + } + + .sdc-modal__close-button { + margin-top: 0px; + width: 20px; + height: 14px; + } + .sdc-modal__close-button-svg { + width: 20px; + height: 20px; + .sdc-modal__svg-use { + fill: $white; + } + .svg-icon { + height: 14px; + width: 14px; + fill: $white; + } + } + } + .sdc-modal__content { + padding: 20px 40px; + } + } + .sdc-modal__header { + padding: 0px 10px 8px 14px; + display: flex; + justify-content: space-between; + text-align: left; + .sdc-modal__icon { + padding: 20px 12px 0px 6px; + } + + .title { + @include heading-2; + flex: 1 1 auto; + color: $text-black; + padding-top: 19px; + } + + .sdc-modal__close-button { + order:3; + width: 14px; + height: 14px; + margin-top:10px; + cursor: pointer; + .sdc-modal__svg-use { + fill: $black; + } + } + } + .sdc-modal__content { + order:2; + padding-left: 63px; + padding-right: 68px; + padding-bottom: 26px; + } + + .sdc-modal__footer { + order:3; + background-color: $light-silver; + border-top: solid 1px $silver; + padding: 17px 30px; + display: flex; + justify-content: flex-end; + margin: 0 -30px; + button{ + margin-left: 10px; + } + } + } +} + +.modal-background { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + background-color: $black; + opacity: 0.70; + z-index: 1000; + + &.show { + z-index: 1000; + opacity: 0.70; + transition: opacity .3s ease, z-index .3s; + } + &.hide { + z-index: -1; + opacity: 0; + transition: opacity .35s ease, z-index .35s; + } +} + +.xl { + width: 1200px; +} + +.l { + width: 875px; +} + +.md { + width: 650px; +} + +.sm { + width: 500px; +} + +.xsm { + width: 432px; +} + diff --git a/src/style/scss/themes/1802/style.scss b/src/style/scss/themes/1802/style.scss new file mode 100644 index 0000000..ae314d8 --- /dev/null +++ b/src/style/scss/themes/1802/style.scss @@ -0,0 +1,5 @@ +@import "../../common"; +@import "components"; + +// for angular +@import "../../angular/svg_icon"; diff --git a/src/style/scss/themes/1802/tabs.scss b/src/style/scss/themes/1802/tabs.scss new file mode 100644 index 0000000..70ee4cb --- /dev/null +++ b/src/style/scss/themes/1802/tabs.scss @@ -0,0 +1,39 @@ +.sdc-tabs { + .sdc-tab { + background-color: $white; + border: 1px solid $silver; + border-left: none; + display: inline-block; + height: 36px; + text-align: center; + cursor: pointer; + padding: 2px 10px 0 10px; + margin: 0; + + + &:first-child { + border-left: 1px solid $silver; + } + &.sdc-tab-active { + background-color: $silver; + } + &[disabled] { + opacity: 0.3; + cursor: default; + } + } + &.sdc-tabs-header { + .sdc-tab { + @include heading-2; + } + } + &.sdc-tabs-menu { + .sdc-tab { + @include body-1; + padding: 0px 10px 4px 10px; + } + } + .sdc-tab-content { + margin-top: 30px; + } +} diff --git a/stories/README.md b/stories/README.md new file mode 100644 index 0000000..7ed8e9d --- /dev/null +++ b/stories/README.md @@ -0,0 +1,9 @@ +# Storybook + +This is the part of the repo that is responsible for defining and building the stories for storybook. Take a look at the following guides for your assistance: + +## Adding a new component to storybook +See [wiki page](https://github.com/onap-sdc/sdc-ui/wiki/Adding-a-new-component-to-storybook). + +## Deploying storybook to a fork's github pages +See [wiki page](https://github.com/onap-sdc/sdc-ui/wiki/Deploying-storybook-to-a-fork's-github-pages). diff --git a/stories/ng2-component-lab/accordion.component.exp.ts b/stories/ng2-component-lab/accordion.component.exp.ts new file mode 100644 index 0000000..480a011 --- /dev/null +++ b/stories/ng2-component-lab/accordion.component.exp.ts @@ -0,0 +1,146 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; +import {Placement} from "../../src/angular/common/enums"; + + +/************************************************** + * Adding custom styles for example + *************************************************/ +const style = document.createElement('style'); +style.innerHTML = ` +.sdc-accordion-custom-class .sdc-accordion-header, +.sdc-accordion-custom-class .sdc-accordion-body.open { + padding: 10px; + border-radius: 3px; +} +.sdc-accordion-custom-class .sdc-accordion-header { + background-color: #d2d2d2; +} +.sdc-accordion-custom-class .sdc-accordion-body.open { + border: 1px solid #d2d2d2; + margin-top: 1px; + } +`; +const head = document.getElementsByTagName('head'); +head[0].appendChild(style); + +export default experimentOn('Accordion').group('Accordion', + [ + { + id: 'simpleAccodion', + title: 'Simple accordion', + description: 'Example of accordion with default left arrow', + showSource: true, + template: ` + +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce consequat dictum porttitor. + Nam facilisis, dui nec maximus facilisis, nisl eros mattis arcu, nec pharetra nisl nisi vitae metus. + Vestibulum urna nunc, fringilla nec imperdiet a, varius hendrerit neque. Aliquam pulvinar turpis enim, ac hendrerit dui blandit eu. + Curabitur ut mollis arcu, ac iaculis turpis. Pellentesque lobortis leo justo. Morbi commodo cursus dignissim. + Nam orci diam, mattis eget leo vel, tincidunt interdum dui. + Donec dapibus mauris non sapien ornare, non pharetra mi commodo. +

    +
    + ` + }, + { + id: 'accordionRightArrow', + title: 'Accordion with right arrow', + description: 'Example of accordion with right arrow', + showSource: true, + context: { + arrowDirection: Placement.right, + }, + template: ` + + + + +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce consequat dictum porttitor. + Nam facilisis, dui nec maximus facilisis, nisl eros mattis arcu, nec pharetra nisl nisi vitae metus. + Vestibulum urna nunc, fringilla nec imperdiet a, varius hendrerit neque. Aliquam pulvinar turpis enim, ac hendrerit dui blandit eu. + Curabitur ut mollis arcu, ac iaculis turpis. Pellentesque lobortis leo justo. Morbi commodo cursus dignissim. + Nam orci diam, mattis eget leo vel, tincidunt interdum dui. + Donec dapibus mauris non sapien ornare, non pharetra mi commodo. +

    +
    + ` + }, + { + id: 'accordionRightArrowStyle', + title: 'Accordion with right arrow and custom style', + description: 'Example of accordion with right arrow and custom style', + showSource: true, + context: { + arrowDirection: Placement.right, + }, + template: ` + +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce consequat dictum porttitor. + Nam facilisis, dui nec maximus facilisis, nisl eros mattis arcu, nec pharetra nisl nisi vitae metus. + Vestibulum urna nunc, fringilla nec imperdiet a, varius hendrerit neque. Aliquam pulvinar turpis enim, ac hendrerit dui blandit eu. + Curabitur ut mollis arcu, ac iaculis turpis. Pellentesque lobortis leo justo. Morbi commodo cursus dignissim. + Nam orci diam, mattis eget leo vel, tincidunt interdum dui. + Donec dapibus mauris non sapien ornare, non pharetra mi commodo. +

    +
    + ` + } + , + { + id: 'accordionLeftArrowStyle', + title: 'Accordion with left arrow and custom style', + description: 'Example of accordion with left arrow and custom style', + showSource: true, + context: { + arrowDirection: Placement.left, + }, + template: ` + +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce consequat dictum porttitor. + Nam facilisis, dui nec maximus facilisis, nisl eros mattis arcu, nec pharetra nisl nisi vitae metus. + Vestibulum urna nunc, fringilla nec imperdiet a, varius hendrerit neque. Aliquam pulvinar turpis enim, ac hendrerit dui blandit eu. + Curabitur ut mollis arcu, ac iaculis turpis. Pellentesque lobortis leo justo. Morbi commodo cursus dignissim. + Nam orci diam, mattis eget leo vel, tincidunt interdum dui. + Donec dapibus mauris non sapien ornare, non pharetra mi commodo. +

    +
    + ` + }, + { + id: 'accordionLeftArrowStyleOpen', + title: 'Open accordion with left arrow and custom style', + description: 'Example of open accordion with left arrow and custom style', + showSource: true, + context: { + arrowDirection: Placement.left, + }, + template: ` + +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce consequat dictum porttitor. + Nam facilisis, dui nec maximus facilisis, nisl eros mattis arcu, nec pharetra nisl nisi vitae metus. + Vestibulum urna nunc, fringilla nec imperdiet a, varius hendrerit neque. Aliquam pulvinar turpis enim, ac hendrerit dui blandit eu. + Curabitur ut mollis arcu, ac iaculis turpis. Pellentesque lobortis leo justo. Morbi commodo cursus dignissim. + Nam orci diam, mattis eget leo vel, tincidunt interdum dui. + Donec dapibus mauris non sapien ornare, non pharetra mi commodo. +

    +
    + ` + } + ]); diff --git a/stories/ng2-component-lab/autocomplete.component.exp.ts b/stories/ng2-component-lab/autocomplete.component.exp.ts new file mode 100644 index 0000000..a1fa3dd --- /dev/null +++ b/stories/ng2-component-lab/autocomplete.component.exp.ts @@ -0,0 +1,77 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; + +export default experimentOn('Autocomplete').group('Autocomplete', +[ + { + id: 'simpleAutocomplete', + title: 'Simple autocomplete data', + description: 'Example of auto complete with simple data', + showSource: true, + context: { + data: ['red', 'yellow', 'orange', 'green', 'white', 'black'], + selectedOption: '', + showSelectedItem: ((value: string) => { + alert(value); + }) + }, + template: ` + + + ` + }, + { + id: 'complexAutocomplete', + title: 'Complex autocomplete data', + description: 'Example of auto complete with complex data', + showSource: true, + context: { + data: [ + {id: 'redId', color: 'red'}, + {id: 'yellowId', color: 'yellow'}, + {id: 'orangeId', color: 'orange'}, + {id: 'greenId', color: 'green'}, + {id: 'whiteId', color: 'white'}, + {id: 'blackId', color: 'black'} + ], + showSelectedItem: ((value: string) => { + alert(value); + }) + }, + template: ` + + + ` + }, + { + id: 'complexAutocompleteWithBeData', + title: 'Complex autocomplete data from server', + description: 'Example of auto complete with complex data from server. (In this example the data is not really filtered, because it is from mock data)', + showSource: true, + context: { + showSelectedItem: ((value: string) => { + alert(value); + }) + }, + template: ` + + + ` + } +]); diff --git a/stories/ng2-component-lab/button.component.exp.ts b/stories/ng2-component-lab/button.component.exp.ts new file mode 100644 index 0000000..6c5fb04 --- /dev/null +++ b/stories/ng2-component-lab/button.component.exp.ts @@ -0,0 +1,164 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; + +const buttonTypes = ['primary', 'secondary', 'link', 'alert']; +const buttonSizes = ['large', 'medium', 'small', 'x-small', 'default']; +const experiment = experimentOn('Button'); + +experiment.group("Default button", [ + { + id: "defaultButton", + showSource: true, + description: `Default button, does not need to supply type or size. +
    The size of the button set to 'default' so it will shrink or expand according to the content. + `, + context: { + buttonClicked: ():void => { + window.alert("OK"); + } + }, + title: "Default button", + template: ` + + + + + + + ` + } +]); + +buttonTypes.forEach((buttonType) => { + [false, true].forEach((buttonDisabled) => { + experiment.group(`Button ${buttonType} ${buttonDisabled ? ' disabled' : ''}`, [ { + id: `Button ${buttonType}${buttonDisabled ? ' disabled' : ''}`, + showSource: true, + context: { + buttonClicked: ():void => { + window.alert("OK"); + } + }, + title: `Button ${buttonType}${buttonDisabled ? ' disabled' : ''}`, + template: buttonSizes.map((buttonSize) => + ` + +
    ${buttonSize}

    + + +
    + `).join('\n') + } + ]); + }); +}); + +experiment.group("Buttons with icons", [ + { + id: "buttonsWithIcons", + showSource: true, + description: `Buttons with icons forward`, + context: { + buttonClicked: (): void => { + window.alert("OK"); + } + }, + title: "Button with icons", + template: ` + + + + + + + + + + + + + ` + } +]); + +experiment.group("Buttons with spinners", [ + { + id: "buttonsWithSpinnersRight", + showSource: true, + description: `Click the button to see the spinner shows for 2 seconds`, + context: { + buttonClicked: (button): void => { + button.show_spinner = true; + setTimeout(() => {button.show_spinner = false},2000); + }, + }, + title: "Button with spinner on the right", + template: ` + + + + ` + }, + { + id: "buttonsWithSpinnersLeft", + showSource: true, + description: `Click the button to see the spinner shows for 2 seconds`, + context: { + buttonClicked: (button): void => { + button.show_spinner = true; + setTimeout(() => {button.show_spinner = false},2000); + }, + }, + title: "Button with spinner on the left", + template: ` + + + + ` + } +]); +export default experiment; diff --git a/stories/ng2-component-lab/checkbox.component.exp.ts b/stories/ng2-component-lab/checkbox.component.exp.ts new file mode 100644 index 0000000..7ac53c9 --- /dev/null +++ b/stories/ng2-component-lab/checkbox.component.exp.ts @@ -0,0 +1,33 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; + +export default experimentOn('Checkbox') + .group("Checkbox",[ + { + id: 'checkbox', + showSource: true, + title: 'Regular Checkbox', + description: 'Simple checkbox', + template: ``, + }, + { + id: 'checkboxChecked', + showSource: true, + title: 'Regular Checked Checkbox', + description: 'Simple checked checkbox', + template: ``, + }, + { + id: 'disabledCheckbox', + showSource: true, + title: 'Disabled checkbox', + description: 'Simple disabled checkbox', + template: ``, + }, + { + id: 'disabledCheckboxChecked', + showSource: true, + title: 'Disabled checked checkbox', + description: 'Simple disabled checked checkbox', + template: ``, + } + ]); diff --git a/stories/ng2-component-lab/checklist.component.exp.ts b/stories/ng2-component-lab/checklist.component.exp.ts new file mode 100644 index 0000000..4700a74 --- /dev/null +++ b/stories/ng2-component-lab/checklist.component.exp.ts @@ -0,0 +1,213 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; +import { ChecklistItemModel } from "../../src/angular/checklist/models/ChecklistItem"; +import { ChecklistModel } from "../../src/angular/checklist/models/Checklist"; + +const styleCode = 'h5{color:red;} pre{background-color: #d1d1d1; padding: 10px;}'; +const checklistValuesExample1 = []; + +const checkListExample1: ChecklistModel = new ChecklistModel(checklistValuesExample1, + [new ChecklistItemModel('apple'), + new ChecklistItemModel('banana'), + new ChecklistItemModel('orange')]); + +const checklistValuesExample2 = []; +const checkListExample2: ChecklistModel = new ChecklistModel(checklistValuesExample2, + [new ChecklistItemModel('apple', false, false, null, 0), + new ChecklistItemModel('banana', false, false, null, 1), + new ChecklistItemModel('orange', false, false, null, 2)]); + +const checklistValuesExample3 = []; +const checkListExample3: ChecklistModel = new ChecklistModel(checklistValuesExample3, + [new ChecklistItemModel('apple', false, true), + new ChecklistItemModel('banana'), + new ChecklistItemModel('orange', false, true)]); + +const checklistValuesExample4 = []; +const checkListExample4: ChecklistModel = new ChecklistModel(checklistValuesExample4, + [new ChecklistItemModel('apple', true, true), + new ChecklistItemModel('banana', true), + new ChecklistItemModel('orange')]); + +const checklistValuesExample5 = []; +const innerChecklistValues = []; +const checkListExample5: ChecklistModel = new ChecklistModel(checklistValuesExample5, + [new ChecklistItemModel('apple', false, false, + new ChecklistModel(innerChecklistValues, + [new ChecklistItemModel('red'), new ChecklistItemModel('green'), new ChecklistItemModel('yellow')])), + new ChecklistItemModel('banana'), + new ChecklistItemModel('orange')]); + +const checklistFirstLevelValuesExample6 = []; +const checklistSecondLevelValuesExample6 = []; +const checklistThirdLevelValuesExample6 = []; +const checkListExample6: ChecklistModel = new ChecklistModel(checklistFirstLevelValuesExample6, + [new ChecklistItemModel('1', false, false, + new ChecklistModel(checklistSecondLevelValuesExample6, [new ChecklistItemModel('1.1'), + new ChecklistItemModel('1.2', false, false, new ChecklistModel(checklistThirdLevelValuesExample6, [new ChecklistItemModel('1.2.1'), + new ChecklistItemModel('1.2.2'), + new ChecklistItemModel('1.2.3')])), + new ChecklistItemModel('1.3')])), + new ChecklistItemModel('2'), + new ChecklistItemModel('3')]); + +export default experimentOn('Checklist') + .group("Checklist",[ + { + id: 'checklist', + showSource: true, + context: { + checklistModel: checkListExample1, + checklistValues: checklistValuesExample1 + }, + styles: [styleCode], + title: 'Checklist', + description: ` +
    +            
    The checklistModel parameter:
    + const checklistValues = []; + const checklistModel: ChecklistModel = + new ChecklistModel(checklistValues, + [new ChecklistItemModel('apple'), + new ChecklistItemModel('banana'), + new ChecklistItemModel('orange')]); +
    + `, + template: ` + Selected values: {{checklistValues.toString()}} + + `, + }, + { + id: 'checklistWithValues', + showSource: true, + context: { + checklistModel: checkListExample2, + checklistValues: checklistValuesExample2 + }, + styles: [styleCode], + title: 'Checklist with values', + description: ` +
    +                
    The checklistModel parameter:
    + const checklistValues = []; + const checklistModel: ChecklistModel = new ChecklistModel(checklistValues, + [new ChecklistItemModel('apple', false, false, null, 0), + new ChecklistItemModel('banana', false, false, null, 1), + new ChecklistItemModel('orange', false, false, null, 2)]); +
    + `, + template: ` + Selected values: {{checklistValues.toString()}} + + ` + }, + { + id: 'checklistWithSomeCheckedItems', + title: 'Checklist with some checked items', + showSource: true, + context: { + checklistModel: checkListExample3, + checklistValues: checklistValuesExample3 + }, + styles: [styleCode], + description: ` +
    The checklistModel parameter:
    + const checklistValues = []; + const checklistModel: ChecklistModel = new ChecklistModel(checklistValues, + [new ChecklistItemModel('apple', false, true), + new ChecklistItemModel('banana'), + new ChecklistItemModel('orange', false, true)]); +
    + `, + template: ` + Selected values: {{checklistValues.toString()}} + + ` + }, + { + id: 'checklistWithSomeDisabledItems', + title: 'Checklist with some disabled items', + showSource: true, + context: { + checklistModel: checkListExample4, + checklistValues: checklistValuesExample4 + }, + styles: [styleCode], + description: ` +
    The checklistModel parameter:
    + const checklistValues = []; + const checklistModel: ChecklistModel = new ChecklistModel(checklistValues, + [new ChecklistItemModel('apple', true, true), + new ChecklistItemModel('banana', true), + new ChecklistItemModel('orange')]); +
    + `, + template: ` + Selected values: {{checklistValues.toString()}} + + ` + }, + { + id: 'twoLevelsChecklist', + title: 'Multi-levels checklist', + showSource: true, + context: { + checklistModel: checkListExample5, + checklistValues: checklistValuesExample5, + innerChecklistValues: innerChecklistValues + }, + styles: [styleCode], + description: ` +
    +            
    The checklistModel parameter:
    + const checklistValues = []; + const innerChecklistValues = []; + const checklistModel: ChecklistModel = new ChecklistModel(checklistValues, + [new ChecklistItemModel('apple', false, false,new ChecklistModel(innerChecklistValues,[new ChecklistItemModel('red'), + new ChecklistItemModel('green'), + new ChecklistItemModel('yellow')])), + new ChecklistItemModel('banana'), + new ChecklistItemModel('orange')]);
    + `, + template: ` +
    Selected values: {{checklistValues.toString()}}
    +
    Inner checklist selected values: {{innerChecklistValues.toString()}}
    + + ` + }, + { + id: 'multiLevelsChecklist', + title: 'Multi-levels checklist', + showSource: true, + context: { + checklistModel: checkListExample6, + checklistFirstLevelValues: checklistFirstLevelValuesExample6, + checklistSecondLevelValues: checklistSecondLevelValuesExample6, + checklistThirdLevelValues: checklistThirdLevelValuesExample6 + }, + styles: [styleCode], + description: ` +
    The checklistModel parameter:
    + const checklistFirstLevelValues = []; + const checklistSecondLevelValues = []; + const checklistThirdLevelValues = []; + const checklistModel: ChecklistModel = new ChecklistModel(checklistFirstLevelValues, + [new ChecklistItemModel('1', false, false, + new ChecklistModel(checklistSecondLevelValues, [new ChecklistItemModel('1.1'), + new ChecklistItemModel('1.2', false, false, + new ChecklistModel(checklistThirdLevelValues, [new ChecklistItemModel('1.2.1'), + new ChecklistItemModel('1.2.2'), + new ChecklistItemModel('1.2.3')])), + new ChecklistItemModel('1.3')])), + new ChecklistItemModel('2'), + new ChecklistItemModel('3')]); +
    + `, + template: ` +
    Selected values: {{checklistFirstLevelValues.toString()}}
    +
    Second level checklist selected values: {{checklistSecondLevelValues.toString()}}
    +
    Third level checklist selected values: {{checklistThirdLevelValues.toString()}}
    + + ` + } + ]); diff --git a/stories/ng2-component-lab/colors.component.exp.ts b/stories/ng2-component-lab/colors.component.exp.ts new file mode 100644 index 0000000..f082d90 --- /dev/null +++ b/stories/ng2-component-lab/colors.component.exp.ts @@ -0,0 +1,42 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; + +const colorMap = { + 'white': '#ffffff', + 'dark-blue': '#0568ae', + 'blue': '#009fdb', + 'light-blue': '#1eb9f3', + 'blue-disabled': '#9dd9ef', + 'lighter-blue': '#e6f6fb', + 'black': '#000000', + 'text-black': '#191919', + 'rich-black': '#323943', + 'dark-gray': '#5a5a5a', + 'gray': '#959595', + 'light-gray': '#d2d2d2', + 'silver': '#eaeaea', + 'light-silver': '#f2f2f2', + 'lighter-silver':'#f8f8f8', + 'green': '#4ca90c', + 'red': '#cf2a2a', + 'light-red':'#ed4141', + 'disabled-red':'#f4adad', + 'yellow': '#ffb81c', + 'dark-purple': '#702f8a', + 'purple': '#9063cd', + 'light-purple': '#caa2dd' +}; + +export default experimentOn('Colors', 1) + .group("Color palette", [ + { + id: 'colorPalette', + showSource: true, + context: { + colorMap + }, + title: 'Color palette', + description: 'Supported design colors', + template: ``, + } + ] + ); diff --git a/stories/ng2-component-lab/components.module.ts b/stories/ng2-component-lab/components.module.ts new file mode 100644 index 0000000..266f047 --- /dev/null +++ b/stories/ng2-component-lab/components.module.ts @@ -0,0 +1,45 @@ +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { FormsModule } from "@angular/forms"; +import { SdcUiComponentsModule } from "../../src/angular"; +import { KeysPipe } from "./utils/pipes/keys.pipe"; +import { SearchFilterPipe } from "./pipes/search-filter-pipe"; +import { ColorsTable } from "./components/colors-table.component"; +import { ModalInnerContent } from "./components/modal-inner-content-example.component"; +import { ModalConsumer } from "./components/modal-consumer.component"; +import { SvgIconsTableComponent } from "./components/svg-icons-table.component"; +import { NotificationsExample } from "./components/notifications-example.component"; +import { Mode, Placement, Size } from "./../../src/angular/common/enums"; + +@NgModule({ + declarations: [ + ColorsTable, + KeysPipe, + ModalInnerContent, + ModalConsumer, + SearchFilterPipe, + SvgIconsTableComponent, + NotificationsExample + ], + imports: [ + CommonModule, + FormsModule, + SdcUiComponentsModule + ], + exports: [ + CommonModule, + SdcUiComponentsModule, + ModalInnerContent, + NotificationsExample, + ColorsTable, + SvgIconsTableComponent, + ModalConsumer, + SearchFilterPipe + ], + entryComponents: [ + ModalInnerContent + ], + providers: [KeysPipe] +}) +export class ComponentsModule { +} diff --git a/stories/ng2-component-lab/components/colors-table.component.ts b/stories/ng2-component-lab/components/colors-table.component.ts new file mode 100644 index 0000000..fc7bd2f --- /dev/null +++ b/stories/ng2-component-lab/components/colors-table.component.ts @@ -0,0 +1,26 @@ +import { Component, Input } from "@angular/core"; + +@Component({ + selector: "colors-table", + template: ` + +

    {{tableTitle}}

    +
    +
    +
    +
    {{color}}
    +
    {{tableMapColors[color]}}
    +
    +
    +` +}) +export class ColorsTable { + + @Input() tableTitle:string; + @Input() tableMapColors: Object; + + constructor() { + + } + +} diff --git a/stories/ng2-component-lab/components/modal-consumer.component.ts b/stories/ng2-component-lab/components/modal-consumer.component.ts new file mode 100644 index 0000000..e4a3977 --- /dev/null +++ b/stories/ng2-component-lab/components/modal-consumer.component.ts @@ -0,0 +1,106 @@ +import { Component, Input, Output, EventEmitter } from "@angular/core"; +import { ModalService } from "../../../src/angular/modals/modal.service"; +import { IModalConfig, ModalType, ModalSize } from "../../../src/angular/modals/models/modal-config"; +import { ModalInnerContent } from "./modal-inner-content-example.component"; +import { ButtonComponent } from "../../../src/angular/buttons/button.component"; +import { ModalButtonComponent } from './../../../src/angular/modals/modal-button.component'; +import { Placement } from "../../../src/angular/common/enums"; +import { ModalComponent } from "../../../src/angular/components"; + +const MODAL_CONTENT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed risus nisl, egestas vitae erat non,' + +'pulvinar lacinia libero. Integer pulvinar pellentesque accumsan. Sed hendrerit lacus eu tempus pharetra'; + +@Component({ + selector: 'modal-consumer', + template: `` +}) +export class ModalConsumer { + @Input() action: string; + + constructor(private modalService: ModalService) { + } + + private openModal = (): void => { + if (this[this.action]) { + this[this.action](); + } + } + + private openErrorModal = (): void => { + this.modalService.openErrorModal(MODAL_CONTENT, "sampleTestId"); + } + + private openAlertModal = (): void => { + this.modalService.openAlertModal("Alert Title", MODAL_CONTENT, 'Continue', this.onConfirmAction, 'sampleTestId'); + } + + private openActionModal = (): void => { + this.modalService.openActionModal('Standard Modal', MODAL_CONTENT, "OK", this.onConfirmAction, "sampleTestId"); + } + + private onConfirmAction = (): void => { + alert("Action has been confirmed"); + } + + private openCustomModal1 = (): void => { + const modalConfig = { + size: ModalSize.medium, + title: 'Title', + type: ModalType.custom, + testId: 'sampleTestIdModal1', + buttons: [ + {id: "saveButton", text: "Save", callback: this.customModalOnSave1, closeModal: false}, + {id: "cancelButton", text: "Cancel", size: 'x-small', type: 'secondary', closeModal: true} + ] as ModalButtonComponent[] + } as IModalConfig; + this.modalService.openCustomModal(modalConfig, ModalInnerContent, {name: "Sample Content"}); + } + + private customModalOnSave1 = (): void => { + const currentInstance: ModalComponent = this.modalService.getCurrentInstance(); + const saveButton: ModalButtonComponent = currentInstance.getButtonById("saveButton"); + saveButton.show_spinner = true; + saveButton.spinner_position = Placement.right; + + // Show spinner for 2 seconds + console.log('Saving example, please wait ...'); + window.setTimeout((button: ModalButtonComponent) => { + button.show_spinner = false; + console.log('Finish saving'); + }, 2000, saveButton); + } + + private openCustomModal2 = (): void => { + const modalConfig = { + size: ModalSize.medium, + title: 'Title', + type: ModalType.custom, + testId: 'sampleTestIdModal2', + buttons: [ + {text: "Change title", callback: this.customModalChangeTitle2, closeModal: false}, + {text: "Change buttons", callback: this.customModalUpdateButtons2, closeModal: false}, + {text: "Disable close", callback: this.customModalUDisableClose2, closeModal: false} + ] + } as IModalConfig; + this.modalService.openCustomModal(modalConfig, ModalInnerContent, {name: "Sample Content"}); + } + + private customModalUDisableClose2 = (): void => { + const currentInstance: ModalComponent = this.modalService.getCurrentInstance(); + currentInstance.getCloseButton().disabled = true; + } + + private customModalChangeTitle2 = (): void => { + const currentInstance: ModalComponent = this.modalService.getCurrentInstance(); + currentInstance.setTitle('New title'); + } + + private customModalUpdateButtons2 = (): void => { + const currentInstance: ModalComponent = this.modalService.getCurrentInstance(); + const newButtons = [ + {text: "Change title", callback: this.customModalChangeTitle2, closeModal: false}, + {text: "Do nothing", closeModal: false} + ] as ModalButtonComponent[]; + currentInstance.setButtons(newButtons); + } +} diff --git a/stories/ng2-component-lab/components/modal-inner-content-example.component.ts b/stories/ng2-component-lab/components/modal-inner-content-example.component.ts new file mode 100644 index 0000000..1b6bed0 --- /dev/null +++ b/stories/ng2-component-lab/components/modal-inner-content-example.component.ts @@ -0,0 +1,16 @@ +import { Component, Input } from "@angular/core"; + +@Component({ + selector: "inner-content", + template: ` +
    + + + +
    +` +}) +export class ModalInnerContent { + + @Input() name:string; +} diff --git a/stories/ng2-component-lab/components/notifications-example.component.ts b/stories/ng2-component-lab/components/notifications-example.component.ts new file mode 100644 index 0000000..91dd95e --- /dev/null +++ b/stories/ng2-component-lab/components/notifications-example.component.ts @@ -0,0 +1,57 @@ +import { Component, Input, ViewChild } from "@angular/core"; +import { NotificationsService } from "../../../src/angular/notifications/services/notifications.service"; +import { NotificationSettings } from "../../../src/angular/notifications/utilities/notification.config"; +import { InnerNotifContent } from "../../../src/angular/notifications/notification-inner-content-example.component"; + +@Component({ + selector: "notifications-example", + template: ` +
    + Send Success Notification + +
    +
    + Send Warning Notification + +
    +
    + Send Info Notification + +
    +
    + Send Success MultipleLine Notification + +
    +
    + Send Success Custom Notification + +
    + + +` +}) +export class NotificationsExample { + + constructor(private notifsService : NotificationsService) { + } + + sendSuccessNotif() { + this.notifsService.push(new NotificationSettings("success", 'notif success message test', 'Notif Title Success')); + } + + sendMultipleLinesSuceessNotif() { + this.notifsService.push(new NotificationSettings("success", 'notif success message test with a lot of test so we can test multiple line case lets just add blabla bcdesfg hijklmnop qrstuvw xyz abcdesfg hijklmnop qrstuvw xyz', 'Notif Title Success')); + } + + sendWarnNotif() { + this.notifsService.push(new NotificationSettings("warn", 'notif warn message test', 'Notif Title Warn')); + } + + sendInfoNotif() { + this.notifsService.push(new NotificationSettings("info", 'notif info message test', 'Notif Title Info')); + } + + sendSuccessCustomNotif() { + this.notifsService.push(new NotificationSettings( "info", 'notif XYZ', 'Notif Custom XYZ', 10000, false, true, InnerNotifContent, { notifyText : "notif info custom inner message test", notifyTitle : "Notif Custom Inner Title Info"})); + } +} diff --git a/stories/ng2-component-lab/components/svg-icons-table.component.ts b/stories/ng2-component-lab/components/svg-icons-table.component.ts new file mode 100644 index 0000000..732650d --- /dev/null +++ b/stories/ng2-component-lab/components/svg-icons-table.component.ts @@ -0,0 +1,189 @@ +import { Component } from "@angular/core"; +import { Mode, Placement, Size } from "../../../src/angular/common/enums"; +import { SvgIconComponent } from "../../../src/angular/svg-icon/svg-icon.component"; +import { IDropDownOption, DropDownOptionType, DropDownTypes } from "../../../src/angular/form-elements/dropdown/dropdown-models"; + +const options1: IDropDownOption[] = [ + { + label: 'First Option', + value: 'First Option', + }, + { + label: 'Second Option', + value: 'Second Option', + }, + { + label: 'Third Option', + value: 'Third Option', + type: DropDownOptionType.Simple + } +]; + +@Component({ + selector: "svg-icons-table", + template: ` +
    +
    + + Selected icon: {{selectedIcon}}None +
    + +
    + +
    +
    + + + +
    +
    +
    + + +
    +
    + +
    + +
    +
    + +
    +
    +                        <svg-icon-label
    +                            [name]="{{selectedIcon}}"
    +                            [mode]="{{mode}}"
    +                            [size]="{{size}}"
    +                            [clickable]="{{clickable}}"
    +                            [disabled]="{{disabled}}"
    +                            [label]="{{label}}"
    +                            [labelPlacement]="{{labelPlacement}}">
    +                        </svg-icon-label>
    +                    
    +
    +
    + +
    +
    +
    + +
    +
    +`, + styles: [` + .svg-icons-table { + display: flex; + flex-flow: row wrap; + justify-content: flex-start; + align-items: stretch; + overflow-y: auto; + } + .svg-icons-table .svg-icon-cell { + border: 1px solid #999; + padding: 5px; + margin: 5px; + width: 250px; + overflow: hidden; + display: flex; + align-items: center; + cursor: pointer; + } + .svg-icons-table .svg-icon-cell.selected { + border-color: #1eb9f3; + background-color: #1eb9f3; + } + .icon-showcase { + margin: 20px 10px; + padding: 10px; + border: 1px solid #999; + background: #eee; + } + .icon-options-wrapper { + display: flex; + flex-flow: row wrap; + justify-content: flex-start; + margin-top: 10px; + } + + .icon-options-checkboxes-wrapper { + display: flex; + flex-flow: row; + margin-top: 10px; + } + + .icon-options-checkboxes { + margin-top: 27px; + margin-right: 30px; + } + + .icon-options-label { + margin-right: 30px; + } + + .icon-code pre { + user-select: text; + } + + sdc-dropdown { + display: inline-block; + min-width: 160px; + } + + sdc-dropdown .sdc-dropdown { + } +`] +}) +export class SvgIconsTableComponent { + public iconsNames: string[]; + public selectedIcon: string; + + public modeOptions; + public sizeOptions; + public labelPlacementOptions; + + private mode: Mode; + private size: Size; + private labelPlacement: Placement; + private clickable: boolean; + private disabled: boolean; + private label: string; + + private defaultIconSettings: {mode: Mode, size: Size}; + + constructor() { + this.iconsNames = Object.keys(SvgIconComponent.Icons); + this.mode = null; + this.size = Size.medium; + this.clickable = false; + this.disabled = false; + this.defaultIconSettings = { mode: Mode.info, size: Size.small }; + + this.modeOptions = [{value: null, label: 'NONE'}].concat(Object.keys(Mode).map((modeKey) => ({ + value: modeKey, + label: Mode[modeKey] + }))); + + this.sizeOptions = Object.keys(Size).map((sizeKey) => ({ + value: sizeKey, + label: Size[sizeKey] + })); + + this.labelPlacementOptions = Object.keys(Placement).map((placementKey) => ({ + value: placementKey, + label: Placement[placementKey] + })); + + this.setDefaults(); + } + + private setDefaults = (): void => { + this.label = 'Some label'; + this.selectedIcon = "attachment"; + this.mode = Mode.primary; + this.labelPlacement = Placement.right; + } + + public selectIcon(iconName) { + this.selectedIcon = iconName; + } +} diff --git a/stories/ng2-component-lab/dropdown.component.exp.ts b/stories/ng2-component-lab/dropdown.component.exp.ts new file mode 100644 index 0000000..025409e --- /dev/null +++ b/stories/ng2-component-lab/dropdown.component.exp.ts @@ -0,0 +1,195 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; +import { IDropDownOption, DropDownOptionType, DropDownTypes } from "../../src/angular/form-elements/dropdown/dropdown-models"; + +const options1: IDropDownOption[] = [ + { + label: 'First Option Label', + value: 'firstOptionValue', + }, + { + label: 'Second Option Label', + value: 'secondOptionValue', + }, + { + label: 'Third Option Label', + value: 'thirdOptionValue', + type: DropDownOptionType.Simple + } +]; + +const options2: IDropDownOption[] = [ + { + label: 'Header Label', + value: 'headerValue', + type: DropDownOptionType.Header + }, + { + label: 'First Option Label', + value: 'firstOptionValue', + type: DropDownOptionType.Simple + }, + { + label: 'Disabled Option Label', + value: 'headerValue', + type: DropDownOptionType.Disable + }, + { + label: 'Second Option Label', + value: 'secondOptionValue', + type: DropDownOptionType.Simple + }, + { + label: 'Ruler Label', + value: 'rulerValue', + type: DropDownOptionType.HorizontalLine + }, + { + label: 'Third Option Label', + value: 'thirdOptionValue', + type: DropDownOptionType.Simple + }, + { + label: 'Fourth Option Label', + value: 'FourthOptionValue', + type: DropDownOptionType.Simple + }, + { + label: 'Fifth Option Label', + value: 'fifthOptionValue', + type: DropDownOptionType.Simple + }, + { + label: 'Ruler Label', + value: 'rulerValue', + type: DropDownOptionType.HorizontalLine + }, + { + label: 'Third Option Label', + value: 'thirdOptionValue', + type: DropDownOptionType.Simple + }, + { + label: 'Fourth Option Label', + value: 'FourthOptionValue', + type: DropDownOptionType.Simple + }, + { + label: 'Fifth Option Label', + value: 'fifthOptionValue', + type: DropDownOptionType.Simple + } +]; + +export default experimentOn('DropDown') + .group("DropDown", [ + { + id: 'normalDropDown', + showSource: true, + context: { + options: options1, + onChange: function(option) { + this.valueSelected = option.value; + } + }, + title: 'Normal DropDown', + description: 'Normal DropDown', + template: ` + +
    Selected value:{{valueSelected}}
    + ` + }, { + id: 'groupDropDown', + showSource: true, + context: { + options: options2, + onChange: function(option) { + this.valueSelected = option.value; + } + }, + title: 'DropDown with groups', + description: 'DropDown with groups', + template: ` + +
    Selected value:{{valueSelected}}
    + ` + }, + { + id: 'groupDropDownPreSelect', + showSource: true, + context: { + options: options2, + onChange: function(option) { + this.valueSelected = option.value; + } + }, + title: 'DropDown with groups and pre-selected value', + description: 'DropDown with groups and pre-selected value', + template: ` + +
    Selected value:{{valueSelected}}
    + ` + }, + { + id: 'headlesspDropDown', + showSource: true, + context: { + options: options2, + dropDownHedlessType: DropDownTypes.Headless, + onChange: function(option) { + this.valueSelected = option.value; + } + }, + title: 'Headless and Labeless DropDown', + description: 'Headless and labeless DropDown', + template: ` + + +
    Selected value:{{valueSelected}}
    + ` + }, + { + id: 'disabledDropDown', + showSource: true, + context: { + options: options2, + onChange: function(option) { + this.valueSelected = option.value; + } + }, + title: 'Disabled DropDown', + description: 'Disabled DropDown', + template: ` + +
    Selected value:{{valueSelected}}
    + ` + }, + { + id: 'normalAutoDropDown', + showSource: true, + context: { + options: options1, + dropDownAutoType: DropDownTypes.Auto, + onChange: function(option) { + this.valueSelected = option.value; + } + }, + title: 'Normal Auto DropDown', + description: 'Normal Auto DropDown', + template: ` + +
    Selected value:{{valueSelected}}
    + ` + } + ]); diff --git a/stories/ng2-component-lab/filter-bar.component.exp.ts b/stories/ng2-component-lab/filter-bar.component.exp.ts new file mode 100644 index 0000000..12a287d --- /dev/null +++ b/stories/ng2-component-lab/filter-bar.component.exp.ts @@ -0,0 +1,56 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; +import { SearchFilterPipe } from './pipes/search-filter-pipe'; + +const action = (e): void => { + console.log("The search query was changed to: ", e); +}; + +export default experimentOn('Filter Bar').group('FilterBar', [ + { + id: 'filterBar', + title: 'Filter bar', + description: ` + The filter bar component text is updated (after debounce time, + default 200 miliseconds) while user write something. + In this example the event on search query changed: + const action = (e): void => { + console.log("The search query was changed to: ", e); + }; + `, + context: { + onChange: action + }, + showSource: true, + template: ` + + +
    + Text to search: {{searchText}} + ` + }, + { + id: 'filterBarWithData', + title: 'Filter bar with data', + description: ` + Example of filter bar component with debounce 100 miliseconds, + and with example pipe for filterring. + `, + context: { + data: ['apple', 'banana', 'orange', 'peach'] + }, + showSource: true, + template: ` + + +
      +
    • {{item}}
    • +
    + ` + } +]); diff --git a/stories/ng2-component-lab/infinite-scroll.component.exp.ts b/stories/ng2-component-lab/infinite-scroll.component.exp.ts new file mode 100644 index 0000000..bd20be5 --- /dev/null +++ b/stories/ng2-component-lab/infinite-scroll.component.exp.ts @@ -0,0 +1,166 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; + +const basicContext = { + scrollContainerId: 'scrollContainer', + numLines: Array(20).fill(null), + hitBottomCount: 0, + pageCount: 0, + isPageLoading: false, + insertPageImmediately: function(pageNum) { + const scrollContainerElem: HTMLElement = document.getElementById(this.scrollContainerId); + scrollContainerElem.appendChild(document.createElement('hr')); + Array(10).fill(null).forEach((_, i) => { + const lineElem = document.createElement('div'); + lineElem.innerHTML = `Page ${pageNum} - line ${i + 1}`; + scrollContainerElem.appendChild(lineElem); + }); + }, + loadPageAsync: function(pageNum, timeout) { + return new Promise((resolve) => { + setTimeout(() => { + this.insertPageImmediately(pageNum); + resolve(); + }, timeout); + }); + }, + onScrollHitBottom: function() { + this.hitBottomCount++; + } +}; + +const basicStyle = ` + .scroll-container { + margin: 12px; + border: none; + padding: 5px; + width: 200px; + height: 100px; + overflow: auto; + font-size: 20px !important; + box-shadow: #666 1px 1px 10px; + } + + .example-source { + background: #eeeeee; + padding: 10px; + border: 1px solid #999999; + } + .example-source pre { + overflow: hidden; + background: #dddddd; + margin-top: 5px; + padding: 5px; + user-select: text; + } +`; +const makeBasicStyleDistance = (distance: number) => ` + .scroll-container::after { + display: block; + content: ''; + height: ${distance}px; + background: red; + } +`; + +export default experimentOn('Infinite-Scroll') + .group("Infinite Scroll",[ + { + id: 'infiniteScrollUsage', + showSource: true, + context: Object.assign({}, basicContext), + title: 'Infinite scroll usage', + description: 'Infinite scroll usage', + styles: [basicStyle], + template: ` +
    +
    + Line {{i + 1}} +
    +
    + Hit bottom for {{hitBottomCount}} times! +
    + onScrollHitBottom declaration: +
    {{onScrollHitBottom}}
    +
    + ` + }, + { + id: 'infiniteScrollUsageWithDistance', + showSource: true, + title: 'Infinite scroll usage with distance', + context: Object.assign({}, basicContext), + styles: [basicStyle, makeBasicStyleDistance(50)], + description: '', + template: ` +
    +
    + Line {{i + 1}} +
    +
    + Hit bottom for {{hitBottomCount}} times! +
    + onScrollHitBottom declaration: +
    {{onScrollHitBottom}}
    +
    + ` + }, + { + id: 'infiniteScrollUsageWithExpandingContent', + title: 'Infinite scroll usage with expanding content', + showSource: true, + context: Object.assign({}, basicContext, { + scrollContainerId: 'scrollContainer1', + onScrollHitBottom: function() { + this.hitBottomCount++; + this.insertPageImmediately(this.pageCount + 1); + this.pageCount++; + } + }), + styles: [basicStyle, makeBasicStyleDistance(20)], + template: ` +
    +
    + Line {{i + 1}} +
    +
    + Hit bottom for {{hitBottomCount}} times!
    + Loaded {{pageCount}} pages! +
    + onScrollHitBottom declaration: +
    {{onScrollHitBottom}}
    +
    + ` + }, + { + id: 'infiniteScrollUsageWithExpandingContentAsynchronous', + title: 'Infinite scroll usage with expanding content asynchronous', + showSource: true, + context: Object.assign({}, basicContext, { + scrollContainerId: 'scrollContainer2', + onScrollHitBottom: function() { + this.hitBottomCount++; + if (!this.isPageLoading) { + this.isPageLoading = true; + this.loadPageAsync(this.pageCount + 1, 5000).then(() => { + this.pageCount++; + this.isPageLoading = false; + }); + } + } + }), + styles: [basicStyle, makeBasicStyleDistance(20)], + template: ` +
    +
    + Line {{i + 1}} +
    +
    + Hit bottom for {{hitBottomCount}} times!
    + Loaded {{pageCount}} pages! LOADING page #{{this.pageCount + 1}} ... +
    + onScrollHitBottom declaration: +
    {{onScrollHitBottom}}
    +
    + ` + } + ]); diff --git a/stories/ng2-component-lab/input.component.exp.ts b/stories/ng2-component-lab/input.component.exp.ts new file mode 100644 index 0000000..7e931d6 --- /dev/null +++ b/stories/ng2-component-lab/input.component.exp.ts @@ -0,0 +1,79 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; + +const valueChange = (value: any): void => { + console.log('The value was changed! >>>>', value); +}; + +export default experimentOn('Input') + .group("Input",[ + { + id: 'normalInput', + showSource: true, + title: 'Normal input', + description: 'Normal input', + template: ` + + ` + }, + { + id: 'disabledInput', + showSource: true, + title: 'Disabled input', + description: 'Disabled input', + template: ` + + ` + }, + { + id: 'InputTypeNumber', + showSource: true, + title: 'Input type number', + description: 'Input type number', + template: ` + + ` + }, + { + id: 'Input required', + title: 'Input required', + description: 'Input required (this add red * to the label, but does not perform validation, use sdc-validation for validation)', + showSource: true, + template: ` + + ` + }, + { + id: 'inputWithMaxLength', + title: 'Input with max length', + description: 'Input with max length', + showSource: true, + template: ` + + ` + }, + { + id: 'inputWithPlaceholder', + title: 'Input with placeholder, custom class, and tests ID', + description: 'Input with placeholder', + showSource: true, + template: ` + + ` + }, + { + id: 'inputWithDebounce', + title: 'Input with debounce time', + description: `
    On value change event code:
    +        const valueChange = (value: any): void => {
    +            console.log('The value was changed! >>>>', value);
    +        };
    +        This event will happen 5 sec after the change
    +        
    `, + showSource: true, + context: { + changeEvent: valueChange + }, + template: ` + + ` + }]); diff --git a/stories/ng2-component-lab/modals.component.exp.ts b/stories/ng2-component-lab/modals.component.exp.ts new file mode 100644 index 0000000..e7e38bc --- /dev/null +++ b/stories/ng2-component-lab/modals.component.exp.ts @@ -0,0 +1,126 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; + +const sourceStyles:string =` + .example-source { + background: #eeeeee; + padding: 10px; + border: 1px solid #999999; + margin-top:30px; + } + .example-source pre { + overflow: hidden; + background: #dddddd; + margin-top: 5px; + padding: 5px; + user-select: text; + } + .example-source pre .comment{ + color:#666; + opacity:0.4; + font-style:italic; + transition: opacity 400ms ease-in; + } + .example-source pre:hover .comment { + opacity:1; + } +`; + +export default experimentOn('Modals') + .group("Modals", [ + { + id: 'standardModal', + showSource: false, + title: 'Standard modal', + description: 'Opens a modal with a custom title, message, and confirm button with a callback.', + template: ` + +
    Source Code: +
    +          const MODAL_CONTENT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed risus nisl, egestas vitae erat non,' +
    +            'pulvinar lacinia libero. Integer pulvinar pellentesque accumsan. Sed hendrerit lacus eu tempus pharetra';
    +
    +          this.modalService.openActionModal('Standard Modal', MODAL_CONTENT, "OK", this.onConfirmAction, "sampleTestId");
    +
    +          private onConfirmAction = ():void => {{ '{' }}
    +            alert("Action has been confirmed");
    +          {{ '}' }};
    +        
    `, + styles: [sourceStyles] + }, + { + id: 'alertModal', + showSource: false, + title: 'Alert modal', + description: 'Opens a standard alert modal with a custom title and message.', + template: ` + + + +
    Source Code: +
    +          const MODAL_CONTENT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed risus nisl, egestas vitae erat non,' +
    +          'pulvinar lacinia libero. Integer pulvinar pellentesque accumsan. Sed hendrerit lacus eu tempus pharetra';
    +
    +          this.modalService.openAlertModal("Alert Title", MODAL_CONTENT, "Continue", this.onConfirmAction, "sampleTestId");
    +        
    `, + styles: [sourceStyles] + }, + { + id: 'errorModal', + showSource: false, + title: 'Error modal', + description: `Opens a standard error modal with a custom message.`, + template: ` +
    Source Code: +
    +
    +          this.modalService.openErrorModal("An error has occurred!", "sampleTestId");
    +        
    `, + styles: [sourceStyles] + }, + { + id: 'customModal1', + showSource: false, + title: 'Custom modal 1', + description: 'Opens a modal with dynamic inner content and customizable title, buttons, and callbacks.', + template: ` + +
    Source Code: +
    +
    +          //create modal config object 
    +          let modalConfig:IModalConfig = {{ '{' }}
    +          size: ModalSize.small,
    +          title: 'Title',
    +          type: ModalType.standard,
    +          buttons: [
    +                    {{ '{' }}text:"Save", size:"'x-small'", callback:this.customModalOnSave, closeModal:false{{ '}' }},
    +                    {{ '{' }}text:"Cancel", size:"'x-small'", closeModal:true{{ '}' }}]
    +            {{ '}' }};
    +
    +          //open modal with dynamically created 'modalInnerContent' example component. Send data object with input 'name'. 
    +          this.modalService.openCustomModal(modalConfig, ModalInnerContent, {{ '{' }}name: "Sample Content"{{ '}' }});
    +
    +          private customModalOnDone = ():void => {{ '{' }}
    +              let currentInstance:any = this.modalService.getCurrentInstance();
    +              alert("Save with result: " + currentInstance.innerModalContent.instance.name);
    +          {{ '}' }};
    +
    +          private customModalOnSave = ():void => {{ '{' }}
    +              let currentInstance:any = this.modalService.getCurrentInstance();
    +              alert("Save with result: " + currentInstance.innerModalContent.instance.name);
    +          {{ '}' }};
    +        
    `, + styles: [sourceStyles] + }, + { + id: 'customModal2', + showSource: false, + title: 'Custom modal 2', + description: 'Opens a modal with, and change his buttons and title', + template: ` + + `, + styles: [sourceStyles] + } + ]); diff --git a/stories/ng2-component-lab/notification.component.exp.ts b/stories/ng2-component-lab/notification.component.exp.ts new file mode 100644 index 0000000..ba2ba24 --- /dev/null +++ b/stories/ng2-component-lab/notification.component.exp.ts @@ -0,0 +1,11 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; + +export default experimentOn('Notification') + .group("Default Notification",[ + { + id: 'notificationContainer', + showSource: true, + title: 'Notification Container', + description: 'container example ...', + template: ``, + }]); diff --git a/stories/ng2-component-lab/pipes/search-filter-pipe.ts b/stories/ng2-component-lab/pipes/search-filter-pipe.ts new file mode 100644 index 0000000..5469eb4 --- /dev/null +++ b/stories/ng2-component-lab/pipes/search-filter-pipe.ts @@ -0,0 +1,15 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe ({ + name: 'PipeSearchFilter', +}) +export class SearchFilterPipe implements PipeTransform { + public transform(value, text: string) { + if (!text || !text.length) { + return value; + } + return value.filter((item) => { + return item.toLowerCase().indexOf(text.toLowerCase()) > -1; + }); + } +} diff --git a/stories/ng2-component-lab/popup-menu.component.exp.ts b/stories/ng2-component-lab/popup-menu.component.exp.ts new file mode 100644 index 0000000..12da361 --- /dev/null +++ b/stories/ng2-component-lab/popup-menu.component.exp.ts @@ -0,0 +1,104 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; + +export default experimentOn('Menu') + .group("Popups",[ + { + id: 'basicPopupMenuStatic', + showSource: true, + title: 'Basic popup menu (static)', + description: 'Basic popup menu (static)', + template: ` + + + + + + + + + + + First + + + + + + + + + + + Selected + + + + + + + + + + + Disabled + + + + + + + + + + + + Second + + + ` + }, + { + id: 'basicMenuRelative', + title: 'Basic menu (relative)', + description: 'Basic menu (relative)', + showSource: true, + context: { + showSelectedItem: (msg, color) => { + const elm = document.getElementById('selectedItem'); + elm.style.color = color; + elm.innerHTML = msg; + } + }, + styles: [` + .message { + position: absolute; + top: 0; left: 0; + color: white; + } + .click-area { + position: absolute; + width: 100%; + height: 100%; + } + `], + template: + ` +
    + Click in the box...
    + (popup menu is {{menuStatus === undefined ? 'never opened' : (menuStatus ? 'open at '+menuPos.x+' , '+menuPos.y : 'closed')}})
    + selected: +
    +
    + + First + Disabled + + Second + Third (none) + +
    +
    + ` + } + ]); diff --git a/stories/ng2-component-lab/radio.component.exp.ts b/stories/ng2-component-lab/radio.component.exp.ts new file mode 100644 index 0000000..aa3959b --- /dev/null +++ b/stories/ng2-component-lab/radio.component.exp.ts @@ -0,0 +1,179 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; + +export default experimentOn('Radios') + .group("Radios",[ + { + id: 'radioButtonsGroupTwoWaysBinding', + showSource: true, + context: { + selectedValue: "val2" + }, + title: 'Radio buttons group (two ways binding)', + description: 'Radio buttons group (two ways binding)', + template: + ` + +
    Selected Radio: {{selectedValue}}
    + ` + }, + { + id: 'radioButtonsGroupDisabled', + title: 'Radio buttons group disabled', + description: 'Radio buttons group disabled', + showSource: true, + context: { + selectedValue: "val1" + }, + template: ` + +
    Selected Radio: {{selectedValue}}
    + ` + }, + { + id: 'radioButtonsGroupPartiallyDisabled', + title: 'Radio buttons group partially disabled', + description: 'Radio buttons group partially disabled', + showSource: true, + context: { + selectedValue: "val2" + }, + template: ` + +
    Selected Radio: {{selectedValue}}
    + ` + }, + { + id: 'radioButtonsGroupVertical', + title: 'Radio buttons group vertical', + description: 'Radio buttons group vertical', + showSource: true, + context: { + selectedValue: "val1" + }, + template: ` + +
    Selected Radio: {{selectedValue}}
    + ` + }, + { + id: 'radioButtonsGroupGetValue', + title: 'Radio buttons group get value', + description: 'Radio buttons group get value', + showSource: true, + context: { + selectedValue: "val1", + getSelectedValue: (val)=>{ + alert(val); + } + }, + template: ` + +
    Selected Radio: {{selectedValue}}
    +
    + ` + }, + { + id: 'radioButtonsGroupSelectValue', + title: 'Radio buttons group select value', + description: 'Radio buttons group select value', + showSource: true, + context: { + selectedValue: "val1" + }, + template: ` + +
    Selected Radio: {{selectedValue}}
    +
    + ` + } + ]); diff --git a/stories/ng2-component-lab/search-bar.component.exp.ts b/stories/ng2-component-lab/search-bar.component.exp.ts new file mode 100644 index 0000000..4a7e1fd --- /dev/null +++ b/stories/ng2-component-lab/search-bar.component.exp.ts @@ -0,0 +1,19 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; + +export default experimentOn('Search Bar').group('SearchBar', [ + { + id: 'search-bar', + title: 'Search Bar', + description: "The search text is updated on click on the magnify", + showSource: true, + template: ` + The text to search: {{searchText}} + + + ` + } +]); diff --git a/stories/ng2-component-lab/svg-icon.component.exp.ts b/stories/ng2-component-lab/svg-icon.component.exp.ts new file mode 100644 index 0000000..c87727d --- /dev/null +++ b/stories/ng2-component-lab/svg-icon.component.exp.ts @@ -0,0 +1,14 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; +import { Mode, Size } from "../../src/angular/common/enums"; + +export default experimentOn('Icons') + .group('SvgIcons', [ + { + id: 'SvgIcons', + showSource: false, + title: 'SVG Icons', + template: ` + + ` + } + ]); diff --git a/stories/ng2-component-lab/tabs.component.exp.ts b/stories/ng2-component-lab/tabs.component.exp.ts new file mode 100644 index 0000000..ff4c0c5 --- /dev/null +++ b/stories/ng2-component-lab/tabs.component.exp.ts @@ -0,0 +1,28 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; + +export default experimentOn('Tabs').group('Tabs', [ + { + id: 'simpleTabs', + title: 'Simple tabs with text title', + description: "Simple tabs with text title", + showSource: true, + template: ` + + This is tab 1 + This is tab 2 + + ` + }, + { + id: 'simpleTabsWithIcons', + title: 'Simple tabs with icon title', + description: "Simple tabs with icon title", + showSource: true, + template: ` + + This is tab 1 + This is tab 2 + + ` + } +]); diff --git a/stories/ng2-component-lab/tag-cloud.component.exp.ts b/stories/ng2-component-lab/tag-cloud.component.exp.ts new file mode 100644 index 0000000..83ad03c --- /dev/null +++ b/stories/ng2-component-lab/tag-cloud.component.exp.ts @@ -0,0 +1,61 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; + +export default experimentOn('Tag Cloud').group('Tag Cloud', [ + { + id: 'list', + title: 'Simple tag-cloud', + description: 'Simple tag-cloud', + showSource: true, + template: ` + + + + ` + }, + { + id: 'unique-tag-cloud', + title: 'List with unique validation', + description: 'List with unique validation', + showSource: true, + template: ` + + + + ` + }, + { + id: 'disabled-tag-cloud', + title: 'Disabled tag-cloud', + description: 'When the parameter isViewOnly = true, the tag-cloud control is disabled', + showSource: true, + template: ` + + + + ` + }, + { + id: 'tag-cloud-with-disabled-items', + title: 'List with some readonly items', + description: 'The parameter isViewOnly can get an array of indexes of tag-cloud items.', + showSource: true, + template: ` + + + + ` + } +]); + diff --git a/stories/ng2-component-lab/tiles.component.exp.ts b/stories/ng2-component-lab/tiles.component.exp.ts new file mode 100644 index 0000000..572b6ca --- /dev/null +++ b/stories/ng2-component-lab/tiles.component.exp.ts @@ -0,0 +1,194 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; + +const alert1 = window.alert; +const console1 = console.log; + +export default experimentOn('Tiles') + .group("Tiles",[ + { + id: 'tileSampleBlue', + showSource: true, + context: { + alert: alert1, + console: console1 + }, + title: 'Tile sample blue', + description: 'Tile sample blue', + template: ` + + +
    P
    +
    + +
    +
    + + + vsp_new_icon + + + + + + + + + + +
    +
    + +
    + + Footer + +
    + ` + }, + { + id: 'tileSamplePurple', + title: 'Tile sample purple', + description: 'Tile sample purple', + showSource: true, + template: ` + + +
    P
    +
    + +
    +
    + + + vsp_new_icon + + + + + + + + + + +
    +
    + +
    + + Footer + +
    + ` + }, + { + id: 'tileSampleWithoutFooter', + title: 'Tile sample without footer', + description: 'Tile sample without footer', + showSource: true, + template: ` + + +
    P
    +
    + +
    +
    + + + vsp_new_icon + + + + + + + + + + +
    +
    + +
    +
    + ` + }, + { + id: 'tileSampleWithoutHeader', + title: 'Tile sample without header', + description: 'Tile sample without header', + showSource: true, + template: ` + + +
    +
    + + + vsp_new_icon + + + + + + + + + + +
    +
    + +
    + + Footer + +
    + ` + }, + { + id: 'tileJustWithInfo', + title: 'Tile just with info', + description: 'Tile just with info', + showSource: true, + template: ` + + +
    +
    + + + vsp_new_icon + + + + + + + + + + +
    +
    + +
    +
    ` + } + ]); diff --git a/stories/ng2-component-lab/tooltip.directive.exp.ts b/stories/ng2-component-lab/tooltip.directive.exp.ts new file mode 100644 index 0000000..9e1dd0b --- /dev/null +++ b/stories/ng2-component-lab/tooltip.directive.exp.ts @@ -0,0 +1,231 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; +import { ArrowPlacement, TooltipPlacement } from '../../src/angular/tooltip/tooltip.directive'; + +const customTemplate = ` + .sdc-custom-tooltip-template-title { + font-size: 20px; + font-weight: bold; + background-color: $black; + color: $white; + text-align: center; + } + + .sdc-custom-tooltip-template-content { + background-color: $black; + color: $white; + display: inline-block; + text-align: center; + } + + .sdc-custom-tooltip-template-image { + width: 100%; + height:100%; + display: inline-block; + text-align: center; + background-color: #ffffff; + } +`; + +export default experimentOn('Tooltip') + .group("Tooltip",[ + { + id: 'leftAlignmentTextTooltip', + showSource: true, + title: 'Tooltip with short text (left placement)', + description: 'left placement', + context: { + placement: TooltipPlacement.Left, + arrowPlacement: ArrowPlacement.LeftTop + }, + template: ` +
    Lorem ipsum dolor sit amet, + show tooltip + + ,consectetur adipiscing elit. Sed risus nisl, egestas vitae erat non, pulvinar lacinia libero. + Integer pulvinar pellentesque accumsan. + show tooltip + + Sed hendrerit lacus eu tempus pharetra +
    + ` + }, + { + id: 'leftAlignmentMultiLineTextTooltip', + showSource: true, + title: 'Tooltip with multi line text (left placement)', + description: 'left placement', + context: { + placement: TooltipPlacement.Left, + arrowPlacement: ArrowPlacement.LeftTop + }, + template: ` +
    + The is text example, + show tooltip + + , more text +
    + ` + }, + { + id: 'customStyleTooltip', + showSource: true, + title: 'Tooltip with custom style', + description: 'Tooltip with custom style, define your class and style it via css.', + context: { + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed risus nisl, egestas vitae erat non, pulvinar lacinia libero. Integer pulvinar pellentesque accumsan. Sed hendrerit lacus eu tempus pharetra' + }, + template: ` + +
    + Some text example, + show tooltip, more text +
    + ` + }, + { + id: 'rightAlignmentHtmlTooltip', + showSource: true, + title: 'Tooltip with HTML template (right placement)', + description: 'right placement', + context: { + placement: TooltipPlacement.Right, + arrowPlacement: ArrowPlacement.LeftTop + }, + styles: [customTemplate], + template: ` + Template Input: +
    
    +                    

    A long text name, very long, long text ...

    + ]]>
    + +
    + The is text example, + show tooltip + + , more text +
    + + + ` + }, + { + id: 'rightAlignmentHtmlCustomStyleTooltip', + showSource: true, + title: 'Tooltip with HTML template and custom style (right placement)', + description: 'right placement', + context: { + placement: TooltipPlacement.Right, + arrowPlacement: ArrowPlacement.LeftTop + }, + styles: [customTemplate], + template: ` + Template Input: +
    Title... Title... Title... Title... Title...

    + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed risus nisl, egestas vitae erat non, pulvinar lacinia libero. Integer pulvinar pellentesque accumsan. Sed hendrerit lacus eu tempus pharetra

    + ]]>
    + +
    + The is text example, + show tooltip + + , more text +
    + + + ` + }, + { + id: 'topAlignmentTextTooltip', + showSource: true, + title: 'Tooltip with text (top placement)', + description: 'top placement', + context: { + placement: TooltipPlacement.Top, + arrowPlacement: ArrowPlacement.LeftTop + }, + template: ` +
    + The is text example, + show tooltip + + , more text +
    + ` + }, + { + id: 'bottomAlignmentHtmlTooltip', + showSource: true, + title: 'Tooltip with HTML template (bottom placement)', + description: 'bottom placement', + context: { + placement: TooltipPlacement.Bottom, + arrowPlacement: ArrowPlacement.LeftTop + }, + template: ` + Template Input: +
    A long text name,
    +
    very long, long text
    + ]]> + +
    + The is text example, + link example + , more text +
    + + ` + }, + ]); diff --git a/stories/ng2-component-lab/utils/mock.json b/stories/ng2-component-lab/utils/mock.json new file mode 100644 index 0000000..6cdaf3b --- /dev/null +++ b/stories/ng2-component-lab/utils/mock.json @@ -0,0 +1,6 @@ +[{"id": "redId", "color": "red"}, +{"id": "yellowId", "color": "yellow"}, +{"id": "orangeId", "color": "orange"}, +{"id": "greenId", "color": "green"}, +{"id": "whiteId", "color": "white"}, +{"id": "blackId", "color": "black"}] diff --git a/stories/ng2-component-lab/utils/pipes/keys.pipe.ts b/stories/ng2-component-lab/utils/pipes/keys.pipe.ts new file mode 100644 index 0000000..2a58cd8 --- /dev/null +++ b/stories/ng2-component-lab/utils/pipes/keys.pipe.ts @@ -0,0 +1,13 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({name: 'keys'}) +export class KeysPipe implements PipeTransform { + transform(value, args:string[]) : any { + let keys = []; + for (let key in value) { + keys.push(key); + } + return keys; + } +} + diff --git a/stories/ng2-component-lab/validation.component.exp.ts b/stories/ng2-component-lab/validation.component.exp.ts new file mode 100644 index 0000000..7f18c93 --- /dev/null +++ b/stories/ng2-component-lab/validation.component.exp.ts @@ -0,0 +1,162 @@ +import { experimentOn } from '@islavi/ng2-component-lab'; +import { RegexPatterns } from '../../src/angular/common/enums'; +import { DropDownOptionType, IDropDownOption } from './../../src/angular/form-elements/dropdown/dropdown-models'; + +const options1: IDropDownOption[] = [ + { + label: 'First Option', + value: 'First Option', + }, + { + label: 'Second Option', + value: 'Second Option', + }, + { + label: 'Third Option', + value: 'Third Option', + type: DropDownOptionType.Simple + } +]; + +export default experimentOn('Validation') + .group("Validation", [ + { + id: 'validation1', + showSource: true, + title: 'Simple validation', + description: 'Simple validation (validating that email is valid and that user inserted something in the field). \ + By default the validation starts after first key press', + context: { + emailPattern: RegexPatterns.email + }, + template: ` + + + + + + ` + }, + { + id: 'validation2', + showSource: true, + title: 'Simple validation', + description: 'Simple validation', + context: { + numbersPattern: RegexPatterns.numbers, + isValueHundred: (value: any) => { + return (Number(value) === 100) ? true : false; + } + }, + template: ` + + + + + + ` + }, + { + id: 'validation3', + showSource: true, + title: 'Disabled validation', + description: 'Disabled validation', + context: { + emailPattern: RegexPatterns.email + }, + template: ` + + + + + + ` + }, + { + id: 'validation4', + showSource: true, + title: 'Validation with value already entered', + description: 'Validation with value already entered', + context: { + emailPattern: RegexPatterns.email + }, + template: ` + + + + + + ` + }, + { + id: 'validation5', + showSource: true, + title: 'Validation with validity changed callback', + description: 'Simple validation with alert when validity changes', + context: { + numbersPattern: RegexPatterns.numbers, + validityChanged: (newState: boolean) => { + alert("Validity has changed to " + newState); + } + }, + template: ` + + + + + ` + }, + { + id: 'dropdownWithValidation', + showSource: true, + context: { + options: options1, + isThirdOption: (value: any) => { + return value === 'Third Option'; + } + }, + title: 'DropDown with validation', + description: 'DropDown with validation', + template: ` + + + + + + ` + }, + { + id: 'validationGroup', + showSource: true, + context: { + options: options1, + emailPattern: RegexPatterns.email, + isThirdOption: (value: any) => { + return value === 'Third Option'; + }, + validateGroup: (validationGroup) => { + validationGroup.validate(); + } + }, + title: 'Validation group', + description: 'Validation group (activating validation from code)', + template: ` + + + + + + + + + + + + + + + + + ` + } + ]); diff --git a/stories/react/Accordion.stories.js b/stories/react/Accordion.stories.js new file mode 100644 index 0000000..85fdae3 --- /dev/null +++ b/stories/react/Accordion.stories.js @@ -0,0 +1,16 @@ +import React from 'react'; +import Examples from './utils/Examples.js'; +import Accordion from '../../src/react/Accordion.js'; +import HTMLBasic from '../../components/accordion/accordion-basic.html'; +let examples = { + Basic: { + jsx:
    Accordion body
    , + html: HTMLBasic + } +}; + +const Checkboxes = () => ( + +); + +export default Checkboxes; diff --git a/stories/react/Checkbox.stories.js b/stories/react/Checkbox.stories.js new file mode 100644 index 0000000..3fb3ad1 --- /dev/null +++ b/stories/react/Checkbox.stories.js @@ -0,0 +1,33 @@ +import React from 'react'; +import Examples from './utils/Examples.js'; + +import Checkbox from '../../src/react/Checkbox'; +import HTMLCheckboxChecked from '../../components/checkbox/checkbox-checked.html'; +import HTMLCheckboxUnchecked from '../../components/checkbox/checkbox-unchecked.html'; +import HTMLCheckboxDisabled from '../../components/checkbox/checkbox-disabled.html'; +import HTMLCheckboxDisabledChecked from '../../components/checkbox/checkbox-disabled-checked.html'; + +let examples = { + Checked: { + jsx: {}} data-test-id='mycheckbox-1' inputRef={() => {} } />, + html: HTMLCheckboxChecked + }, + Unchecked: { + jsx: {}} data-test-id='mycheckbox-2' inputRef={() => {} } />, + html: HTMLCheckboxUnchecked + }, + Disabled: { + jsx: {}} data-test-id='mycheckbox-4' inputRef={() => {} } />, + html: HTMLCheckboxDisabled + }, + 'Disabled and Checked': { + jsx: {}} data-test-id='mycheckbox-4' inputRef={() => {} } />, + html: HTMLCheckboxDisabledChecked + } +}; + +const Checkboxes = () => ( + +); + +export default Checkboxes; diff --git a/stories/react/Checklist.stories.js b/stories/react/Checklist.stories.js new file mode 100644 index 0000000..0fd089b --- /dev/null +++ b/stories/react/Checklist.stories.js @@ -0,0 +1,65 @@ +import React from 'react'; +import Examples from './utils/Examples.js'; +import Checklist from '../../src/react/Checklist.js'; +import HTMLListChecked from '../../components/checklist/checklist-with-checked-items.html'; +import HTMLListDisabled from '../../components/checklist/checklist-with-disabled-items.html'; +const items = [ + { + label: 'apple', + value: 'apple', + dataTestId: 'apple', + checked: true + }, + { + label: 'banana', + value: 'banana', + dataTestId: 'banana', + checked: false + }, + { + label: 'orange', + value: 'orange', + dataTestId: 'orange', + checked: true + } +]; + +const itemsDisabled = [ + { + label: 'apple', + value: 'apple', + dataTestId: 'apple', + checked: true, + disabled: true + }, + { + label: 'banana', + value: 'banana', + dataTestId: 'banana', + checked: false, + disabled: true + }, + { + label: 'orange', + value: 'orange', + dataTestId: 'orange', + checked: false + } +]; + +let examples = { + Basic: { + jsx: { }} />, + html: HTMLListChecked + }, + Disabled: { + jsx: { }} />, + html: HTMLListDisabled + } +}; + +const ChecklistStory = () => ( + +); + +export default ChecklistStory; \ No newline at end of file diff --git a/stories/react/Colors.stories.js b/stories/react/Colors.stories.js new file mode 100644 index 0000000..d6758ce --- /dev/null +++ b/stories/react/Colors.stories.js @@ -0,0 +1,53 @@ +import React, {Component} from 'react'; + +const colorMap = { + '$white': '#ffffff', + '$blue': '#009fdb', + '$light-blue': '#1eb9f3', + '$lighter-blue': '#e6f6fb', + '$blue-disabled': '#9dd9ef', + '$dark-blue': '#0568ae', + '$black': '#000000', + '$rich-black': '#323943', + '$text-black': '#191919', + '$dark-gray': '#5a5a5a', + '$gray': '#959595', + '$light-gray': '#d2d2d2', + '$silver': '#eaeaea', + '$light-silver': '#f2f2f2', + '$green': '#4ca90c', + '$functional-red': '#cf2a2a', + '$yellow': '#ffb81c', + '$dark-purple': '#702f8a', + '$purple': '#9063cd', + '$light-purple': '#caa2dd' +}; + +function Color({colorName, colorValue}) { + return ( +
    +
    +
    {colorName.replace('$', '')}
    +
    {colorValue}
    +
    + ); +} + +class Colors extends Component { + + render() { + return ( +
    +

    Colors Palette

    +
    + { + Object.keys(colorMap).map(colorName => ) + } +
    +
    + ); + } + +} + +export default Colors; diff --git a/stories/react/Input.stories.js b/stories/react/Input.stories.js new file mode 100644 index 0000000..869bafa --- /dev/null +++ b/stories/react/Input.stories.js @@ -0,0 +1,51 @@ +import React from 'react'; +import { action } from '@storybook/addon-actions'; +import Examples from './utils/Examples.js'; + +import ReactInput from '../../src/react/Input.js'; + +import InputDefaultHtml from '../../components/input/input.html'; +import InputRequiredHtml from '../../components/input/input-required.html'; +import InputNumberHtml from '../../components/input/input-number.html'; +import InputViewOnlyHtml from '../../components/input/input-view-only.html'; +import InputDisabledHtml from '../../components/input/input-disabled.html'; +import InputPlaceholderHtml from '../../components/input/input-placeholder.html'; +import InputErrorHtml from '../../components/input/input-error.html'; + +let examples = { + 'Input Default': { + jsx: , + html: InputDefaultHtml + }, + 'Input Required': { + jsx: , + html: InputRequiredHtml + }, + 'Input Number': { + jsx: , + html: InputNumberHtml + }, + 'Input View Only': { + jsx: , + html: InputViewOnlyHtml + }, + 'Input Disabled': { + jsx: , + html: InputDisabledHtml + }, + 'Input Placeholder': { + jsx: , + html: InputPlaceholderHtml + }, + 'Input Error': { + jsx: , + html: InputErrorHtml + } + +} + +const Inputs = () => ( + +); + +export default Inputs; diff --git a/stories/react/Modal.stories.js b/stories/react/Modal.stories.js new file mode 100644 index 0000000..29ff7a5 --- /dev/null +++ b/stories/react/Modal.stories.js @@ -0,0 +1,133 @@ +import React from 'react'; +import Examples from './utils/Examples.js'; +import Button from '../../src/react/Button.js'; +import Modal from '../../src/react/Modal.js'; +import Input from '../../src/react/Input.js'; +import HTMLStandardModal from '../../components/modal/standard-modal.html'; +import HTMLAlertModal from '../../components/modal/alert-modal.html'; +import HTMLErrorModal from '../../components/modal/error-modal.html'; +import HTMLCustomModal from '../../components/modal/custom-modal.html'; + +class Example extends React.Component { + constructor(props) { + super(props); + this.state = { + show: false + }; + } + render() { + const { children } = this.props; + const { show } = this.state; + var childrenWithProps = React.Children.map(children, child => { + let childChildrenWithProps = []; + if (child.props.children) { + let childChildren = child.props.children; + childChildrenWithProps = React.Children.map(childChildren, child => + React.cloneElement(child, { onClose: ()=>this.setState({show: !show}) })); + + } + return React.cloneElement(child, { show: this.state.show, onClose: ()=>this.setState({show: !show}), children: childChildrenWithProps}); + } + ); + + return ( +
    + + {childrenWithProps} +
    + ); + } +} + +const ModalBody = () => { + return ( +
    + + + + +
    ); +}; + +const BODY_TEXT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed risus nisl, egestas vitae erat non,' + + 'pulvinar lacinia libero. Integer pulvinar pellentesque accumsan. Sed hendrerit lacus eu tempus pharetra'; + +const isShown = false; + +let examples = { + Standard: { + jsx: + isShown()} size='small'> + Standard Modal + + {BODY_TEXT} + + {}}/> + + , + html: HTMLStandardModal, + exclude: 'Example', + renderFromJsx: true + }, + Alert: { + jsx: + isShown()} type='alert' size='small'> + Title + + {BODY_TEXT} + + + + , + html: HTMLAlertModal, + exclude: 'Example', + renderFromJsx: true + }, + Error: { + jsx: + isShown()} size='small' type='error'> + isShown(false)} type='error'>Title + + {BODY_TEXT} + + isShown(false)} closeButtonText='Ok'/> + + , + html: HTMLErrorModal, + exclude: 'Example', + renderFromJsx: true + }, + + Custom: { + jsx: + isShown()} type='custom'> + Title + + + + {}}/> + + , + html: HTMLCustomModal, + exclude: 'Example', + renderFromJsx: true + } +}; + +const Modals = () => ( + +); + +export default Modals; \ No newline at end of file diff --git a/stories/react/Panel.stories.js b/stories/react/Panel.stories.js new file mode 100644 index 0000000..f87eefb --- /dev/null +++ b/stories/react/Panel.stories.js @@ -0,0 +1,22 @@ +import React from 'react'; +import Examples from './utils/Examples.js'; +import Panel from '../../src/react/Panel.js'; +import Checkbox from '../../src/react/Checkbox.js'; +import HTMLBasic from '../../components/panel/basic-panel.html'; +let examples = { + Basic: { + jsx: + +

    Panel

    + + +
    , + html: HTMLBasic + } +}; + +const PanelStory = () => ( + +); + +export default PanelStory; diff --git a/stories/react/PopupMenu.stories.js b/stories/react/PopupMenu.stories.js new file mode 100644 index 0000000..9d94522 --- /dev/null +++ b/stories/react/PopupMenu.stories.js @@ -0,0 +1,37 @@ + +import React from 'react'; +import Examples from './utils/Examples.js'; +import PopupMenu from '../../src/react/PopupMenu.js'; +import PopupMenuItem from '../../src/react/PopupMenuItem.js'; +import HTMLPopupMenu from '../../components/menu/popup-menu.html'; +import HTMLPopupMenuRelative from '../../components/menu/relative-popup-menu.html'; + +let examples = { + 'Basic popup menu (static)': { + jsx: {}}> + + + + + + , + html: HTMLPopupMenu + }, + 'Basic popup menu (relative)': { + jsx:
    + {}} position={{x: 10, y: 10}} relative> + + + + + +
    , + html: HTMLPopupMenuRelative + } +}; + +const PopupMenuReactComponent = () => ( + +); + +export default PopupMenuReactComponent; \ No newline at end of file diff --git a/stories/react/Radio.stories.js b/stories/react/Radio.stories.js new file mode 100644 index 0000000..151f947 --- /dev/null +++ b/stories/react/Radio.stories.js @@ -0,0 +1,33 @@ +import React from 'react'; +import Examples from './utils/Examples.js'; + +import Radio from '../../src/react/Radio'; +import HTMLRadioChecked from '../../components/radio/radio-checked.html'; +import HTMLRadioUnchecked from '../../components/radio/radio-unchecked.html'; +import HTMLRadioDisabled from '../../components/radio/radio-disabled.html'; +import HTMLRadioDisabledChecked from '../../components/radio/radio-disabled-checked.html'; + +let examples = { + Checked: { + jsx: {}} data-test-id='myradio-1' inputRef={() => {} } />, + html: HTMLRadioChecked + }, + Unchecked: { + jsx: {}} data-test-id='myradio-2' inputRef={() => {} } />, + html: HTMLRadioUnchecked + }, + Disabled: { + jsx: {}} data-test-id='myradio-4' inputRef={() => {} } />, + html: HTMLRadioDisabled + }, + 'Disabled and Checked': { + jsx: {}} data-test-id='myradio-4' inputRef={() => {} } />, + html: HTMLRadioDisabledChecked + } +}; + +const Radios = () => ( + +); + +export default Radios; diff --git a/stories/react/RadioGroup.stories.js b/stories/react/RadioGroup.stories.js new file mode 100644 index 0000000..912f9b9 --- /dev/null +++ b/stories/react/RadioGroup.stories.js @@ -0,0 +1,34 @@ +import React from 'react'; +import Examples from './utils/Examples.js'; + +import RadioGroup from '../../src/react/RadioGroup'; +import HTMLRadioGroup from '../../components/radioGroup/radio-group.html'; +import HTMLRadioGroupValue from '../../components/radioGroup/radio-group-value.html'; +import HTMLRadioGroupDisabled from '../../components/radioGroup/radio-group-disabled.html'; +import HTMLRadioGroupNoTitle from '../../components/radioGroup/radio-group-no-title.html'; + +let examples = { + 'Value': { + jsx: {}} data-test-id='grp2' options={[{value: '1', label: 'option 1'}, {value: '2', label: 'option 2'}]} />, + html: HTMLRadioGroupValue + }, + 'No Value': { + jsx: {}} data-test-id='grp3' options={[{value: '1', label: 'option 1'}, {value: '2', label: 'option 2'}]} />, + html: HTMLRadioGroup + }, + 'Disabled': { + jsx: {}} data-test-id='grp4' options={[{value: '1', label: 'option 1'}, {value: '2', label: 'option 2'}]} />, + html: HTMLRadioGroupDisabled + }, + 'No title': { + jsx: {}} data-test-id='grp4' options={[{value: '1', label: 'option 1'}, {value: '2', label: 'option 2'}]} />, + html: HTMLRadioGroupNoTitle + } + +}; + +const RadioGroups = () => ( + +); + +export default RadioGroups; diff --git a/stories/react/SVGIcon.stories.js b/stories/react/SVGIcon.stories.js new file mode 100644 index 0000000..2c2ffc2 --- /dev/null +++ b/stories/react/SVGIcon.stories.js @@ -0,0 +1,103 @@ +import React from 'react'; +import Examples from './utils/Examples.js'; +import DropdownMenu from './utils/components/DropdownMenu.js'; +import SVGIcon from '../../src/react/SVGIcon.js'; + +const iconLabelPositions = [ + '', 'bottom', 'top', 'left', 'right' +]; + +const iconColors = [ + '', + 'primary', + 'secondary', + 'positive', + 'negative', + 'warning' +]; + +const disabledStates = ['false', 'true']; + +function buildExamples({iconName, iconLabel, labelPosition, color, disabled}) { + return { + Example: { + jsx: + } + }; +} + +const IconTable = ({onClick}) => ( +
    + {ICON_NAMES.map(icon => ( +
    + onClick(icon)} + label={icon} + iconClassName='storybook-small' + name={icon} /> +
    + ))} +
    +); + +class Icons extends React.Component { + constructor(props) { + super(props); + this.state = { + iconName: ICON_NAMES[0], + iconLabel: '', + labelPosition: iconLabelPositions[0], + color : iconColors[0] + }; + } + + render() { + let {iconName, iconLabel, labelPosition, color, disabled} = this.state; + return ( +
    +

    Icons

    +
    + this.setState({iconName: e.target.value})} + options={ICON_NAMES} /> +
    + + this.setState({iconLabel: e.target.value})}/> +
    + this.setState({labelPosition: e.target.value})} + options={iconLabelPositions} /> + this.setState({color: e.target.value})} + options={iconColors} /> + this.setState({disabled: e.target.value})} + options={disabledStates} /> +
    + + this.setState({iconName: icon})} /> +
    +
    You will see the following if the icon name you used is not found:
    + {}} + name='MissingIcon' /> +
    +
    + ); + }; +} + +export default Icons; diff --git a/stories/react/Tabs.stories.js b/stories/react/Tabs.stories.js new file mode 100644 index 0000000..74f163c --- /dev/null +++ b/stories/react/Tabs.stories.js @@ -0,0 +1,48 @@ +import React from 'react'; +import Examples from './utils/Examples.js'; +import {default as TabsComp} from '../../src/react/Tabs.js'; +import Tab from '../../src/react/Tab.js'; +import HTMLTabsHeader from '../../components/tabs/tabs-header.html'; +import HTMLTabsDisabled from '../../components/tabs/tabs-disabled.html'; +import HTMLTabsMenu from '../../components/tabs/tabs-menu.html'; + +let examples = { + 'Menu Tabs': { + jsx: {console.log(tabId);}}> + +
    This is the active tab content
    +
    + + +
    , + html: HTMLTabsMenu + }, + 'Header Tabs': { + jsx: {console.log(tabId);}}> + +
    This is the active tab content
    +
    + + +
    , + html: HTMLTabsHeader + }, + 'Disabled Tabs': { + jsx: ( + {console.log(tabId);}}> + +
    This is the active tab content
    +
    + + +
    + ), + html: HTMLTabsDisabled + } +}; + +const Tabs = () => ( + +); + +export default Tabs; diff --git a/stories/react/Tiles.stories.js b/stories/react/Tiles.stories.js new file mode 100644 index 0000000..04a6fb5 --- /dev/null +++ b/stories/react/Tiles.stories.js @@ -0,0 +1,89 @@ +import React from 'react'; + +import Examples from './utils/Examples.js'; +import SVGIcon from '../../src/react/SVGIcon.js'; +import Button from '../../src/react/Button.js'; + +import Tile from '../../src/react/Tile.js'; +import TileInfo from '../../src/react/TileInfo.js'; +import TileInfoLine from '../../src/react/TileInfoLine.js'; +import TileFooter from '../../src/react/TileFooter.js'; +import TileFooterCell from '../../src/react/TileFooterCell.js'; + +import HTMLTileWithoutFooter from '../../components/tile/tile-without-footer.html'; +import HTMLVspTile from '../../components/tile/vsp-tile.html'; +import HTMLVlmTile from '../../components/tile/vlm-tile.html'; +import HTMLVendorTile from '../../components/tile/vendor-tile.html'; +import HTMLVfcTile from '../../components/tile/vfc-tile.html'; + +let examples = { + 'Without footer': { + jsx: + + Supertitle + Title + + , + html: HTMLTileWithoutFooter + }, + VFC: { + jsx: + + Title + V 1.0 + + + Certified + + , + html: HTMLVfcTile + }, + VSP: { + jsx: + + VLM + VSP name + + + Draft + + , + html: HTMLVspTile + }, + VLM: { + jsx: + + VLM name + + + Certified + + + + + , + html: HTMLVlmTile + }, + Vendor: { + jsx: + + Vendor name + + + + + + + + + + , + html: HTMLVendorTile + }, +}; + +const Tiles = () => ( + +); + +export default Tiles; diff --git a/stories/react/Typography.stories.js b/stories/react/Typography.stories.js new file mode 100644 index 0000000..f1475c6 --- /dev/null +++ b/stories/react/Typography.stories.js @@ -0,0 +1,62 @@ +import React, {Component} from 'react'; + +const typos = [ + {className: 'heading-1', size: 28, text: 'Major Section Heading'}, + {className: 'heading-2', size: 24, text: 'Sub-Section Heading'}, + {className: 'heading-3', size: 20, text: 'Small Heading'}, + {className: 'heading-4', size: 16, text: 'Small Heading'}, + {className: 'heading-4-emphasis', size: 16, text: 'Small Heading'}, + {className: 'heading-5', size: 14, text: 'Small Heading'}, + {className: 'body-1', size: 14, text: 'Body (Standard) Text'}, + {className: 'body-1-italic', size: 14, text: 'Body (Standard) Text'}, + {className: 'body-2', size: 13, text: 'Text in Tables'}, + {className: 'body-2-emphasis', size: 13, text: 'Text in Tables'}, + {className: 'body-3', size: 12, text: 'Input Labels, Table Titles'}, + {className: 'body-3-emphasis', size: 12, text: 'Even Smaller Text'}, + {className: 'body-4', size: 10, text: 'Even Much Smaller Text'} +]; + +const fontWeights = ['OpenSans Regular 400', 'OpenSans Semibold 600']; + +function TextRow({className, size, text}) { + return ( +
    +
    {className}
    +
    {size}px
    +
    {text}
    +
    + ); +} + +class Typography extends Component { + + render() { + return ( +
    +

    Typography

    +
    +

    Font Family

    +
      +
    • OpenSans
    • +
    • Arial
    • +
    • sans-serif
    • +
    +
    +
    +

    Font Weights

    +
      {fontWeights.map(font =>
    • {font}
    • )}
    +
    +
    +

    Font Size

    +
    + + {typos.map(typo => )} +
    +
    +
    + ); + } + +} + +export default Typography; diff --git a/stories/react/buttons/LinkButtons.stories.js b/stories/react/buttons/LinkButtons.stories.js new file mode 100644 index 0000000..ef32a22 --- /dev/null +++ b/stories/react/buttons/LinkButtons.stories.js @@ -0,0 +1,49 @@ +import React from 'react'; +import Examples from '../utils/Examples.js'; + +import ReactButton from '../../../src/react/Button.js'; + +import LinkButton from '../../../components/button/button-link.html'; +import LinkButtonDisabled from '../../../components/button/button-link-disabled.html'; +import ExtraSmall from '../../../components/button/button-link-extra-small.html'; +import Small from '../../../components/button/button-link-small.html'; +import Medium from '../../../components/button/button-link-medium.html'; +import Large from '../../../components/button/button-link-large.html'; +import Auto from '../../../components/button/button-link-auto.html'; + +let examples = { + 'Link Default': { + jsx: {}}>Click Me, + html: LinkButton + }, + 'Link Disabled': { + jsx: {}} disabled>Click Me, + html: LinkButtonDisabled, + }, + 'Extra Small': { + jsx: {}}>Click Me, + html: ExtraSmall + }, + 'Small': { + jsx: {}}>Click Me, + html: Small, + }, + 'Medium': { + jsx: {}}>Click Me, + html: Medium + }, + 'Large': { + jsx: {}}>Click Me, + html: Large, + }, + 'Auto Sizing': { + jsx: {}}>Click Me, + html: Auto, + } +}; + +const DefaultButtons = () => ( + +); + +export default DefaultButtons; diff --git a/stories/react/buttons/PrimaryButtons.stories.js b/stories/react/buttons/PrimaryButtons.stories.js new file mode 100644 index 0000000..db732b9 --- /dev/null +++ b/stories/react/buttons/PrimaryButtons.stories.js @@ -0,0 +1,49 @@ +import React from 'react'; +import Examples from '../utils/Examples.js'; + +import ReactButton from '../../../src/react/Button.js'; + +import PrimaryButton from '../../../components/button/button-primary.html'; +import PrimaryButtonDisabled from '../../../components/button/button-primary-disabled.html'; +import ExtraSmall from '../../../components/button/button-primary-extra-small.html'; +import Small from '../../../components/button/button-primary-small.html'; +import Medium from '../../../components/button/button-primary-medium.html'; +import Large from '../../../components/button/button-primary-large.html'; +import Auto from '../../../components/button/button-primary-auto.html'; + +let examples = { + 'Primary Default': { + jsx: {}}>Click Me, + html: PrimaryButton + }, + 'Primary Disabled': { + jsx: {}} disabled>Click Me, + html: PrimaryButtonDisabled, + }, + 'Extra Small': { + jsx: {}}>Click Me, + html: ExtraSmall + }, + 'Small': { + jsx: {}}>Click Me, + html: Small, + }, + 'Medium': { + jsx: {}}>Click Me, + html: Medium + }, + 'Large': { + jsx: {}}>Click Me, + html: Large, + }, + 'Auto Sizing': { + jsx: {}}>Click Me, + html: Auto, + } +}; + +const DefaultButtons = () => ( + +); + +export default DefaultButtons; diff --git a/stories/react/buttons/SecondaryButtons.stories.js b/stories/react/buttons/SecondaryButtons.stories.js new file mode 100644 index 0000000..75f9d54 --- /dev/null +++ b/stories/react/buttons/SecondaryButtons.stories.js @@ -0,0 +1,49 @@ +import React from 'react'; +import Examples from '../utils/Examples.js'; + +import ReactButton from '../../../src/react/Button.js'; + +import SecondaryButton from '../../../components/button/button-secondary.html'; +import SecondaryButtonDisabled from '../../../components/button/button-secondary-disabled.html'; +import ExtraSmall from '../../../components/button/button-secondary-extra-small.html'; +import Small from '../../../components/button/button-secondary-small.html'; +import Medium from '../../../components/button/button-secondary-medium.html'; +import Large from '../../../components/button/button-secondary-large.html'; +import Auto from '../../../components/button/button-secondary-auto.html'; + +let examples = { + 'Secondary Default': { + jsx: {}}>Click Me, + html: SecondaryButton + }, + 'Secondary Disabled': { + jsx: {}} disabled>Click Me, + html: SecondaryButtonDisabled, + }, + 'Extra Small': { + jsx: {}}>Click Me, + html: ExtraSmall + }, + 'Small': { + jsx: {}}>Click Me, + html: Small, + }, + 'Medium': { + jsx: {}}>Click Me, + html: Medium + }, + 'Large': { + jsx: {}}>Click Me, + html: Large, + }, + 'Auto Sizing': { + jsx: {}}>Click Me, + html: Auto, + } +}; + +const DefaultButtons = () => ( + +); + +export default DefaultButtons; diff --git a/stories/react/index.js b/stories/react/index.js new file mode 100644 index 0000000..6d425ba --- /dev/null +++ b/stories/react/index.js @@ -0,0 +1,66 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; + +import PrimaryButtons from './buttons/PrimaryButtons.stories.js'; +import SecondaryButtons from './buttons/SecondaryButtons.stories.js'; +import LinkButtons from './buttons/LinkButtons.stories.js'; + +import Colors from './Colors.stories.js'; +import Typography from './Typography.stories.js'; +import Checkboxes from './Checkbox.stories.js'; +import Checklist from './Checklist.stories.js'; +import Input from './Input.stories.js'; +import Icons from './SVGIcon.stories.js'; +import Tiles from './Tiles.stories.js'; +import Tabs from './Tabs.stories.js'; +import Radios from './Radio.stories.js'; +import RadioGroups from './RadioGroup.stories.js'; +import Modals from './Modal.stories.js'; +import PopupMenu from './PopupMenu.stories.js'; +import Accordion from './Accordion.stories.js'; +import Panel from './Panel.stories.js'; + +storiesOf('Colors', module) + .add('Color Palette', () => ); + +storiesOf('Typography', module) + .add('Typography', () => ); + +storiesOf('Accordion', module) + .add('Accordion', () => ); + +storiesOf('Buttons', module) + .add('Primary', () => ) + .add('Secondary', () => ) + .add('Link', () => ); + +storiesOf('Checkboxes', module) + .add('Checkboxes', () => ); + +storiesOf('Checklist', module) + .add('Checklist', () => ); + +storiesOf('Input Fields', module) + .add('Input Text', () => ); + +storiesOf('Icons', module) + .add('SVG Icons', () => ); + +storiesOf('Menu', module) + .add('Popup Menu', () => ); + +storiesOf('Modals', module) + .add('Modal examples', () => ); + +storiesOf('Radios', module) + .add('Radio Buttons', () => ) + .add('Radio Button Groups', () => ); + +storiesOf('Panel', module) + .add('Panel', () => ); + +storiesOf('Tabs', module) + .add('Tabs', () => ); + +storiesOf('Tiles', module) + .add('Tiles', () => ); diff --git a/stories/react/utils/BeautifyHTML.js b/stories/react/utils/BeautifyHTML.js new file mode 100644 index 0000000..1a29b00 --- /dev/null +++ b/stories/react/utils/BeautifyHTML.js @@ -0,0 +1,33 @@ +export default function beautifyHTML({html, indentChar = ' ', startingIndentCount = 0}) { + html = html.replace(/[ ]{2,}/g, ' '); + + let result = '', indentCount = startingIndentCount, parsingText = false; + for (let i = 0; i < html.length; i++) { + + let startOfTag, endOfTag, closingTag, upcomingTag, afterTag, numTabs; + if (html[i] === '<') { startOfTag = true; } + else if (html[i] === '>') { endOfTag = true; } + else if (html[i - 1] === '>') { afterTag = true; } + if (html[i + 1] === '/') { closingTag = true; } + else if (html[i + 1 ] === '<') { upcomingTag = true; } + + if (startOfTag) { + if (closingTag) { numTabs = --indentCount; } + else { numTabs = indentCount++; } + } + + if (parsingText && afterTag) { + numTabs = indentCount; + } + + result += indentChar.repeat(numTabs) + html[i]; + + if (endOfTag || parsingText && upcomingTag) { + result += '\n'; + parsingText = false; + if (!upcomingTag) { parsingText = true; } + } + } + + return result.slice(0, -1); +} diff --git a/stories/react/utils/Examples.js b/stories/react/utils/Examples.js new file mode 100644 index 0000000..5948b68 --- /dev/null +++ b/stories/react/utils/Examples.js @@ -0,0 +1,23 @@ +import React from 'react'; +import {renderToStaticMarkup} from 'react-dom/server'; +import SourceToggle from './SourceToggle.js'; +import beautifyHTML from './BeautifyHTML.js'; +import insertSVGIcons from './InsertSVGIcons.js'; + +const Examples = ({examples}) => ( +
    + {Object.keys(examples).map(key => { + let title = key; + let {jsx, html, displayTitle = true, exclude, renderFromJsx = false} = examples[key]; + if (!html) { + html = renderToStaticMarkup(jsx); + html = beautifyHTML({html, indentChar: ' '}); + } else { + html = insertSVGIcons({html, jsx}); + } + return ; + })} +
    +); + +export default Examples; diff --git a/stories/react/utils/InsertSVGIcons.js b/stories/react/utils/InsertSVGIcons.js new file mode 100644 index 0000000..5a5e390 --- /dev/null +++ b/stories/react/utils/InsertSVGIcons.js @@ -0,0 +1,15 @@ +import {renderToStaticMarkup} from 'react-dom/server'; +import beautifyHTML from './BeautifyHTML.js'; + +const insertSVGIcons = ({html, jsx, indentChar = ' '}) => { + let svgCode = renderToStaticMarkup(jsx).match(/(]*>)[\s\S]*?(<\/svg>)/g); + let newHTML = html.replace(/\s*/g, str => { + let html = '\n' + svgCode.shift(); + let indentRegExp = new RegExp(`[${indentChar}]*`); + let startingIndentCount = str.slice(2).match(indentRegExp)[0].length / indentChar.length; + return beautifyHTML({html, startingIndentCount, indentChar}); + }); + return newHTML; +}; + +export default insertSVGIcons; diff --git a/stories/react/utils/SourceToggle.js b/stories/react/utils/SourceToggle.js new file mode 100644 index 0000000..a05c8d0 --- /dev/null +++ b/stories/react/utils/SourceToggle.js @@ -0,0 +1,73 @@ +/* eslint-disable react/no-danger */ +import React from 'react'; +import jsxToString from './jsxToString.js'; + +import Prism from 'prismjs'; + +import PrismJsx from 'prismjs/components/prism-jsx.js'; // eslint-disable-line no-unused-vars + +const sources = { + React: 'React', + HTML: 'HTML' +}; + +export default class SourceToggle extends React.Component { + constructor(props) { + super(props); + this.state = { + source: sources.React + }; + } + + renderFromSource() { + let {jsx, html, renderFromJsx} = this.props; + let {source} = this.state; + let classname = 'source-toggle-example'; + switch (source) { + case sources.HTML: + return renderFromJsx ?
    {jsx}
    :
    ; + case sources.React: + default: + return
    {jsx}
    ; + } + } + + renderMarkdown() { + let {jsx, html, exclude} = this.props; + let {source} = this.state; + switch (source) { + case sources.HTML: + return {__html: Prism.highlight(html, Prism.languages.html)}; + case sources.React: + default: + return {__html: Prism.highlight(jsxToString({jsx, exclude}), Prism.languages.jsx)}; + } + } + + render() { + let {title} = this.props; + return ( +
    + {title &&
    {title}
    } +
    + {this.renderFromSource()} +
    +
    + {Object.keys(sources).map((source, i) => ( +
    this.setState({source})}> + {source} +
    + ))} +
    +
    +							
    +						
    +
    +
    +
    + ); + } +} diff --git a/stories/react/utils/components/DropdownMenu.js b/stories/react/utils/components/DropdownMenu.js new file mode 100644 index 0000000..4a69463 --- /dev/null +++ b/stories/react/utils/components/DropdownMenu.js @@ -0,0 +1,14 @@ +import React from 'react'; + +const DropdownMenu = ({title, value, onChange, options}) => ( +
    + + +
    +); + +export default DropdownMenu; diff --git a/stories/react/utils/jsxToString.js b/stories/react/utils/jsxToString.js new file mode 100644 index 0000000..8b799ad --- /dev/null +++ b/stories/react/utils/jsxToString.js @@ -0,0 +1,74 @@ +import React, {Children} from 'react'; + +const INDENT = ' '; + +function stringRepresentationForJsx(item) { + if (typeof item === 'string') { + return `'${item}'`; + } else if (typeof item === 'number') { + return item.toString(); + } else if (Array.isArray(item)) { + return `[${item.map(val => stringRepresentationForJsx(val)).toString()}]`; + } + else if (typeof item === 'boolean') { + return item.toString(); + } + else if (typeof item === 'function') { + return item.toString().replace(/\s{2,}/g, ' '); + } else if (typeof item === 'object') { + let repr = '{'; + for (let key in item) { + if (item.hasOwnProperty(key)) { + repr += `${key}: ${stringRepresentationForJsx(item[key])}, `; + } + } + repr = repr.slice(0, -2); + repr += '}'; + return repr; + } +} + +function parseProps(jsx, indentCount) { + let result = ''; + for (let prop in jsx.props) { + let value = jsx.props[prop]; + if (prop !== 'children' && value) { + let repr = stringRepresentationForJsx(value); + let isString = repr.startsWith("'"); + result += `\n${INDENT.repeat(indentCount)}${prop}`; + if (value !== true) { + result += `=${isString ? '' : '{ '}${stringRepresentationForJsx(value)}${isString ? '' : ' }'}`; + } + } + } + return result; +} + +function jsxToString({jsx, indentCount=0, exclude}) { + if (typeof jsx === 'string'){ + return jsx; + } + + let name = typeof jsx.type === 'string' ? jsx.type : jsx.type.name; + let result = name === exclude ? '' + : `${INDENT.repeat(indentCount)}<${name}${parseProps(jsx, indentCount + 1)}`; + + if (jsx.props.hasOwnProperty('children')) { + let {children} = jsx.props; + let childrenArr = Children.toArray(children); + if (name !== exclude) { result += '>\n';} + if (typeof children === 'string') { + result += `${INDENT.repeat(indentCount + 1)}${children}\n`; + } else { + let newIndentCount = name === exclude ? indentCount : indentCount + 1; + childrenArr.forEach(child => result += `${jsxToString({jsx: child, indentCount: newIndentCount})}\n`); + } + const closingTag = name === exclude ? '' + : `${INDENT.repeat(indentCount)}`; + return result + closingTag; + } + + return result + ' />'; +} + +export default jsxToString; diff --git a/test/react/Accordion.spec.js b/test/react/Accordion.spec.js new file mode 100644 index 0000000..614dcdd --- /dev/null +++ b/test/react/Accordion.spec.js @@ -0,0 +1,11 @@ +import React from 'react'; +import Accordion from '../../src/react/Accordion.js'; + +import renderer from 'react-test-renderer'; + +describe('Accordion', () => { + test('Accordion - Default', () => { + const accordion = renderer.create(Accordion body).toJSON(); + expect(accordion).toMatchSnapshot(); + }); +}); \ No newline at end of file diff --git a/test/react/Button.spec.js b/test/react/Button.spec.js new file mode 100644 index 0000000..3b3b72e --- /dev/null +++ b/test/react/Button.spec.js @@ -0,0 +1,77 @@ +import React from 'react'; +import Button from '../../src/react/Button.js'; + +import renderer from 'react-test-renderer'; + +describe('Button', () => { + test('Button - Default - Primary', () => { + const button = renderer.create().toJSON(); + expect(button).toMatchSnapshot(); + }); + + test('Button - Default - Primary - Disabled', () => { + const button = renderer.create().toJSON(); + expect(button).toMatchSnapshot(); + }); + + test('Button - Default - White', () => { + const button = renderer.create().toJSON(); + expect(button).toMatchSnapshot(); + }); + + test('Button - Default - Gray', () => { + const button = renderer.create().toJSON(); + expect(button).toMatchSnapshot(); + }); + + test('Button - Default - Positive', () => { + const button = renderer.create().toJSON(); + expect(button).toMatchSnapshot(); + }); + + test('Button - Default - Negative', () => { + const button = renderer.create().toJSON(); + expect(button).toMatchSnapshot(); + }); + + test('Button - Default - Warning', () => { + const button = renderer.create().toJSON(); + expect(button).toMatchSnapshot(); + }); + + test('Button - Outline - Primary', () => { + const button = renderer.create().toJSON(); + expect(button).toMatchSnapshot(); + }); + + test('Button - Outline - Gray', () => { + const button = renderer.create().toJSON(); + expect(button).toMatchSnapshot(); + }); + + test('Button - Outline - Positive', () => { + const button = renderer.create().toJSON(); + expect(button).toMatchSnapshot(); + }); + + test('Button - Outline - Negative', () => { + const button = renderer.create().toJSON(); + expect(button).toMatchSnapshot(); + }); + + test('Button - Link - Primary', () => { + const button = renderer.create().toJSON(); + expect(button).toMatchSnapshot(); + }); + + test('Button - Link - Primary - Disabled', () => { + const button = renderer.create().toJSON(); + expect(button).toMatchSnapshot(); + }); + + test('Button - Link - Primary - With Icon', () => { + const button = renderer.create().toJSON(); + expect(button).toMatchSnapshot(); + }); + +}); diff --git a/test/react/Checkbox.spec.js b/test/react/Checkbox.spec.js new file mode 100644 index 0000000..88ba660 --- /dev/null +++ b/test/react/Checkbox.spec.js @@ -0,0 +1,60 @@ +import React from 'react'; +import Checkbox from '../../src/react/Checkbox.js'; + +import renderer from 'react-test-renderer'; +import {mount} from 'enzyme'; + +class CheckboxForm extends React.Component { + constructor(props) { + super(props); + this.state = {checked: false}; + + this.handleChange = this.handleChange.bind(this); + } + + handleChange(val) { + this.setState({checked: val}); + } + + getChecked() { + return this.checkbox.getChecked(); + } + + render() { + return ( +
    + {this.checkbox = checkbox;}} checked={this.state.checked} onChange={this.handleChange} label='This is the checkbox label' /> + + ); + } +} + +describe('Checkbox', () => { + test('Checkbox - unchecked', () => { + const checkbox = renderer.create().toJSON(); + expect(checkbox).toMatchSnapshot(); + }); + + test('Checkbox - disabled', () => { + const checkbox = renderer.create().toJSON(); + expect(checkbox).toMatchSnapshot(); + }); + + test('Checkbox - checked state changes', () => { + const checkbox = mount(); + expect(checkbox.instance().getChecked()).toEqual(false); + expect(checkbox.instance().getChecked()).toEqual(checkbox.find('input').props().checked); + checkbox.find('input').simulate('change', { target : { checked: true }}); + expect(checkbox.instance().getChecked()).toEqual(checkbox.find('input').props().checked); + expect(checkbox.instance().getChecked()).toEqual(true); + checkbox.find('input').simulate('change', { target : { checked: false }}); + expect(checkbox.instance().getChecked()).toEqual(false); + expect(checkbox.instance().getChecked()).toEqual(checkbox.find('input').props().checked); + }); + + test('Checkbox - returns its value', () => { + const checkbox = mount(); + expect(checkbox.instance().getValue()).toEqual('myVal'); + }); + +}); diff --git a/test/react/Checklist.spec.js b/test/react/Checklist.spec.js new file mode 100644 index 0000000..a3409ad --- /dev/null +++ b/test/react/Checklist.spec.js @@ -0,0 +1,64 @@ +import React from 'react'; +import Checklist from '../../src/react/Checklist.js'; + +import renderer from 'react-test-renderer'; + +const items = [ + { + label: 'apple', + value: 'apple', + dataTestId: 'apple', + checked: true + }, + { + label: 'banana', + value: 'banana', + dataTestId: 'banana', + checked: false + }, + { + label: 'orange', + value: 'orange', + dataTestId: 'orange', + checked: true + } +]; + +const itemsDisabled = [ + { + label: 'apple', + value: 'apple', + dataTestId: 'apple', + checked: true, + disabled: true + }, + { + label: 'banana', + value: 'banana', + dataTestId: 'banana', + checked: false, + disabled: true + }, + { + label: 'orange', + value: 'orange', + dataTestId: 'orange', + checked: false + } +]; + + +describe('Checklist', () => { + test('Checklist - Default ', () => { + const checklist = renderer.create( { }} />).toJSON(); + expect(checklist).toMatchSnapshot(); + }); + + test('Checklist - With disabled items ', () => { + const checklist = renderer.create( { }} />).toJSON(); + expect(checklist).toMatchSnapshot(); + }); + + + +}); \ No newline at end of file diff --git a/test/react/Input.spec.js b/test/react/Input.spec.js new file mode 100644 index 0000000..3766e45 --- /dev/null +++ b/test/react/Input.spec.js @@ -0,0 +1,69 @@ +import React from 'react'; +import Input from '../../src/react/Input.js'; + +import renderer from 'react-test-renderer'; +import {mount} from 'enzyme'; + +class InputForm extends React.Component { + constructor(props) { + super(props); + this.state = {value: 'Initial'}; + this.handleChange = this.handleChange.bind(this); + } + + handleChange(val) { + this.setState({value: val}); + } + + getValue() { + return this.input.getValue(); + } + + render() { + return ( +
    + {this.input = input;}} value={this.state.value} name='testinput' onChange={this.handleChange} /> +
    + ); + } +} + +describe('Input', () => { + test('Input - default', () => { + const input = renderer.create().toJSON(); + expect(input).toMatchSnapshot(); + }); + + test('Input - required', () => { + const input = renderer.create().toJSON(); + expect(input).toMatchSnapshot(); + }); + + test('Input - number', () => { + const input = renderer.create().toJSON(); + expect(input).toMatchSnapshot(); + }); + + test('Input - readonly', () => { + const input = renderer.create().toJSON(); + expect(input).toMatchSnapshot(); + }); + + test('Input - placeholder', () => { + const input = renderer.create().toJSON(); + expect(input).toMatchSnapshot(); + }); + + test('Input - error', () => { + const input = renderer.create().toJSON(); + expect(input).toMatchSnapshot(); + }); + + test('Input - checked state value changes', () => { + const input = mount(); + expect(input.instance().getValue()).toEqual('Initial'); + input.find('input').simulate('change', { target : { value: 'Changed' }}); + expect(input.instance().getValue()).toEqual('Changed'); + expect(input.find('input').prop('value')).toEqual('Changed'); + }); +}); diff --git a/test/react/Modal.spec.js b/test/react/Modal.spec.js new file mode 100644 index 0000000..7c4738f --- /dev/null +++ b/test/react/Modal.spec.js @@ -0,0 +1,68 @@ +import React from 'react'; +import { mount, ReactWrapper } from 'enzyme'; + +import Modal from '../../src/react/Modal'; + +describe('Modal', () => { + + const MODAL_MESSAGE = 'Message'; + test('standard modal', ()=>{ + const modal = new ReactWrapper(mount( + Standard Modal + + {MODAL_MESSAGE} + + {}}/> + ).instance().modalRef, true); + + expect(modal.find(Modal.Body).length).toBe(1); + expect(modal.find(Modal.Header).length).toBe(1); + expect(modal.find(Modal.Title).length).toBe(1); + expect(modal.find(Modal.Body).length).toBe(1); + expect(modal.find(Modal.Footer).length).toBe(1); + expect(modal.find(Modal.Header).props().type).toBe('info'); + expect(modal.find(Modal.Body).text()).toBe(MODAL_MESSAGE); + expect(modal.html()).toMatchSnapshot(); + }); + + test('standard modal - not displayed', ()=>{ + const modal = new ReactWrapper(mount( + Standard Modal + + {MODAL_MESSAGE} + + {}}/> + ).instance().modalRef, true); + expect(modal.find(Modal.Body).length).toBe(0); + expect(modal.html()).toMatchSnapshot(); + }); + + test('alert modal', ()=>{ + const modal = new ReactWrapper(mount( + + Title + + {MODAL_MESSAGE} + + + ).instance().modalRef, true); + expect(modal.find(Modal.Body).text()).toBe(MODAL_MESSAGE); + expect(modal.find('.sdc-modal-type-alert').length).toBe(1); + expect(modal.html()).toMatchSnapshot(); + }); + + test('custom modal', ()=>{ + const modal = new ReactWrapper(mount( + + Title + + {MODAL_MESSAGE} + + {}}/> + ).instance().modalRef, true); + expect(modal.find(Modal.Body).text()).toBe(MODAL_MESSAGE); + expect(modal.find('.sdc-modal-type-custom').length).toBe(1); + expect(modal.html()).toMatchSnapshot(); + }); + +}); diff --git a/test/react/ModalBody.spec.js b/test/react/ModalBody.spec.js new file mode 100644 index 0000000..d83c899 --- /dev/null +++ b/test/react/ModalBody.spec.js @@ -0,0 +1,11 @@ +import React from 'react'; +import ModalBody from '../../src/react/ModalBody.js'; + +import renderer from 'react-test-renderer'; + +describe('ModalBody', () => { + test('basic test', () => { + const header = renderer.create().toJSON(); + expect(header).toMatchSnapshot(); + }); +}); \ No newline at end of file diff --git a/test/react/ModalFooter.spec.js b/test/react/ModalFooter.spec.js new file mode 100644 index 0000000..e4e3f5b --- /dev/null +++ b/test/react/ModalFooter.spec.js @@ -0,0 +1,11 @@ +import React from 'react'; +import ModalFooter from '../../src/react/ModalFooter.js'; + +import renderer from 'react-test-renderer'; + +describe('ModalFooter', () => { + test('basic test', () => { + const footer = renderer.create().toJSON(); + expect(footer).toMatchSnapshot(); + }); +}); \ No newline at end of file diff --git a/test/react/ModalHeader.spec.js b/test/react/ModalHeader.spec.js new file mode 100644 index 0000000..e9d0602 --- /dev/null +++ b/test/react/ModalHeader.spec.js @@ -0,0 +1,11 @@ +import React from 'react'; +import ModalHeader from '../../src/react/ModalHeader.js'; + +import renderer from 'react-test-renderer'; + +describe('ModalHeader', () => { + test('basic test', () => { + const header = renderer.create().toJSON(); + expect(header).toMatchSnapshot(); + }); +}); \ No newline at end of file diff --git a/test/react/ModalTitle.spec.js b/test/react/ModalTitle.spec.js new file mode 100644 index 0000000..a8cf2a3 --- /dev/null +++ b/test/react/ModalTitle.spec.js @@ -0,0 +1,11 @@ +import React from 'react'; +import ModalTitle from '../../src/react/ModalTitle.js'; + +import renderer from 'react-test-renderer'; + +describe('ModalTitle', () => { + test('basic test', () => { + const header = renderer.create(Title).toJSON(); + expect(header).toMatchSnapshot(); + }); +}); \ No newline at end of file diff --git a/test/react/Panel.spec.js b/test/react/Panel.spec.js new file mode 100644 index 0000000..acae6a9 --- /dev/null +++ b/test/react/Panel.spec.js @@ -0,0 +1,11 @@ +import React from 'react'; +import Panel from '../../src/react/Panel.js'; + +import renderer from 'react-test-renderer'; + +describe('Panel', () => { + test('Panel - Default', () => { + const panel = renderer.create(Panel).toJSON(); + expect(panel).toMatchSnapshot(); + }); +}); diff --git a/test/react/PopupMenu.spec.js b/test/react/PopupMenu.spec.js new file mode 100644 index 0000000..5014728 --- /dev/null +++ b/test/react/PopupMenu.spec.js @@ -0,0 +1,62 @@ +import React from 'react'; +import PopupMenu from '../../src/react/PopupMenu.js'; + +import renderer from 'react-test-renderer'; +import {mount} from 'enzyme'; + +class MyPopupForm extends React.Component { + constructor(props) { + super(props); + this.state = {position: {}}; + } + + handleClick(newPos) { + this.setState({position: newPos}); + } + + getPosition() { + return this.state.position; + } + + render() { + return ( +
    this.handleClick({ + x: event.pageX - event.target.offsetLeft, + y: event.pageY - event.target.offsetTop + })}> + {}} /> + + ); + } +} + +describe('PopupMenu', () => { + test('check static menu rendered', () => { + const menu = renderer.create( {}}/>).toJSON(); + expect(menu).toMatchSnapshot(); + }); + + test('check relative menu rendered', () => { + const menu = renderer.create( {}} position={{x: 10, y: 10}} relative/>).toJSON(); + expect(menu).toMatchSnapshot(); + }); + + test('check separator rendered', () => { + const separator = renderer.create().toJSON(); + expect(separator).toMatchSnapshot(); + }); + + test('check position changed', () => { + const menuForm = mount(); + const position = menuForm.instance().getPosition(); + expect(position).toEqual({}); + expect(position).toEqual(menuForm.find('ul').props().style); + menuForm.find('form').simulate('click', { + target: {offsetLeft: 10, offsetTop: 20}, + pageX: 30, pageY: 50 + }); + const newPosition = menuForm.instance().getPosition(); + expect(newPosition).toEqual({x:20, y:30}); + expect({left: newPosition.x, top: newPosition.y}).toEqual(menuForm.find('ul').props().style); + }); +}); \ No newline at end of file diff --git a/test/react/PopupMenuItem.spec.js b/test/react/PopupMenuItem.spec.js new file mode 100644 index 0000000..9262319 --- /dev/null +++ b/test/react/PopupMenuItem.spec.js @@ -0,0 +1,56 @@ +import React from 'react'; +import PopupMenuItem from '../../src/react/PopupMenuItem.js'; +import PopupMenu from '../../src/react/PopupMenu.js'; + +import renderer from 'react-test-renderer'; +import {mount} from 'enzyme'; + +describe('PopupMenuItem', () => { + test('check selected', () => { + const menuItem = renderer.create().toJSON(); + expect(menuItem).toMatchSnapshot(); + }); + + test('check disabled', () => { + const menuItem = renderer.create().toJSON(); + expect(menuItem).toMatchSnapshot(); + }); + + test('check parent onclick invoked by child', () => { + const mockFunc = jest.fn(); + const menu = mount( + + + + ); + expect(menu.find('li')).toHaveLength(1); + menu.find('li').simulate('click'); + expect(mockFunc).toHaveBeenCalled(); + }); + + test('check custom onclick invoked by child', () => { + const mockParentFunc = jest.fn(); + const mockChildFunc = jest.fn(); + const menu = mount( + + + + ); + expect(menu.find('li')).toHaveLength(1); + menu.find('li').simulate('click'); + expect(mockChildFunc).toHaveBeenCalled(); + expect(mockParentFunc).not.toHaveBeenCalled(); + }); + + test('check no click handler if item is disabled', () => { + const mockFunc = jest.fn(); + const menu = mount( + + + + ); + expect(menu.find('li')).toHaveLength(1); + menu.find('li').simulate('click'); + expect(mockFunc).not.toHaveBeenCalled(); + }); +}); \ No newline at end of file diff --git a/test/react/Portal.spec.js b/test/react/Portal.spec.js new file mode 100644 index 0000000..864b342 --- /dev/null +++ b/test/react/Portal.spec.js @@ -0,0 +1,10 @@ +import React from 'react'; +import {mount} from 'enzyme'; +import Portal from '../../src/react/Portal'; + +describe('Portal',()=>{ + test('portal basic', ()=>{ + const portal = mount(Message); + expect(portal.find(Portal).exists()).toBe(true); + }); +}); \ No newline at end of file diff --git a/test/react/Radio.spec.js b/test/react/Radio.spec.js new file mode 100644 index 0000000..39e33f7 --- /dev/null +++ b/test/react/Radio.spec.js @@ -0,0 +1,60 @@ +import React from 'react'; +import Radio from '../../src/react/Radio.js'; + +import renderer from 'react-test-renderer'; +import {mount} from 'enzyme'; + +class RadioForm extends React.Component { + constructor(props) { + super(props); + this.state = {checked: false}; + + this.handleChange = this.handleChange.bind(this); + } + + handleChange(val) { + this.setState({checked: val}); + } + + getChecked() { + return this.radio.getChecked(); + } + + render() { + return ( +
    + {this.radio = radio;}} name='grp1' checked={this.state.checked} label='This is the radio label' value='1' onChange={this.handleChange} /> + + ); + } +} + +describe('Radio', () => { + test('Radio - unchecked', () => { + const radio = renderer.create().toJSON(); + expect(radio).toMatchSnapshot(); + }); + + test('Radio - disabled', () => { + const radio = renderer.create().toJSON(); + expect(radio).toMatchSnapshot(); + }); + + test('Radio - checked state changes', () => { + const radio = mount(); + expect(radio.instance().getChecked()).toEqual(false); + expect(radio.instance().getChecked()).toEqual(radio.find('input').props().checked); + radio.find('input').simulate('change', { target : { checked: true }}); + expect(radio.instance().getChecked()).toEqual(true); + expect(radio.instance().getChecked()).toEqual(radio.find('input').props().checked); + radio.find('input').simulate('change', { target : { checked: false }}); + expect(radio.instance().getChecked()).toEqual(false); + expect(radio.instance().getChecked()).toEqual(radio.find('input').props().checked); + }); + + test('Radio - returns its value', () => { + const radio = mount(); + expect(radio.instance().getValue()).toEqual('myVal'); + }); + +}); diff --git a/test/react/RadioGroup.spec.js b/test/react/RadioGroup.spec.js new file mode 100644 index 0000000..638b9c4 --- /dev/null +++ b/test/react/RadioGroup.spec.js @@ -0,0 +1,69 @@ +import React from 'react'; +import RadioGroup from '../../src/react/RadioGroup.js'; + +import renderer from 'react-test-renderer'; +import {mount} from 'enzyme'; + +class RadioGroupForm extends React.Component { + constructor(props) { + super(props); + this.state = {value: undefined}; + this.handleChange = this.handleChange.bind(this); + } + + handleChange(val) { + this.setState({value: val}); + } + + getValue() { + return this.grp.getValue(); + } + + render() { + return ( +
    + { this.grp = grp;}} onChange={this.handleChange} data-test-id='grp1' + options={[{value: '1', label: 'option 1'}, {value: '2', label: 'option 2'}]} /> + + ); + } +} + +describe('RadioGroup', () => { + test('RadioGroup - basic rendering', () => { + const radio = renderer.create({}} data-test-id='grp1' + options={[{value: '1', label: 'option 1'}, {value: '2', label: 'option 2'}]} />).toJSON(); + expect(radio).toMatchSnapshot(); + }); + + test('RadioGroup - value overrides default value', () => { + const radio = mount({}} data-test-id='grp1' + options={[{value: '1', label: 'option 1'}, {value: '2', label: 'option 2'}]} />); + expect(radio.instance().getValue()).toEqual('1'); + }); + + test('RadioGroup - can have no value', () => { + const radio = mount({}} data-test-id='grp1' + options={[{value: '1', label: 'option 1'}, {value: '2', label: 'option 2'}]} />); + expect(radio.instance().getValue()).toEqual(undefined); + }); + + test('RadioGroup - can be rendered without title', () => { + const radio = mount({}} data-test-id='grp1' + options={[{value: '1', label: 'option 1'}, {value: '2', label: 'option 2'}]} />); + expect(radio.find('.sdc-radio-group__legend').length).toEqual(0); + }); + + test('RadioGroup - value changes', () => { + const radio = mount(); + expect(radio.instance().getValue()).toEqual(undefined); + radio.find('input[value="1"]').simulate('change', { target : { checked: true }}); + expect(radio.instance().getValue()).toEqual('1'); + radio.find('input[value="2"]').simulate('change', { target : { checked: true }}); + expect(radio.instance().getValue()).toEqual('2'); + }); +}); diff --git a/test/react/Tabs.spec.js b/test/react/Tabs.spec.js new file mode 100644 index 0000000..9906708 --- /dev/null +++ b/test/react/Tabs.spec.js @@ -0,0 +1,55 @@ +import React from 'react'; +import Tab from '../../src/react/Tab.js'; +import Tabs from '../../src/react/Tabs.js'; + +import renderer from 'react-test-renderer'; +import {mount} from 'enzyme'; + +class TabsForm extends React.Component { + constructor(props) { + super(props); + this.state = {tabId: '1'}; + + this.handleChange = this.handleChange.bind(this); + } + + handleChange(val) { + this.setState({tabId: val}); + } + + render() { + return ( +
    +
    + this.tabsInst = tabs} activeTab={this.state.tabId} onTabClick={this.handleChange} > + Tab #1 + Tab #2 + Tab #3 + +
    + +
    + ); + } +} + +describe('Tabs', () => { + + test('Tabs - basic rendering', () => { + const tabs = renderer.create().toJSON(); + expect(tabs).toMatchSnapshot(); + }); + + test('Tabs - when active tab id is changed, the respective tab is shown', () => { + const tabs = mount(); + expect(tabs.instance().tabsInst.props.activeTab).toEqual('1'); + expect(tabs.find('.sdc-tab-content').text()).toEqual('Tab #1'); + expect(tabs.find('li[data-test-id="1"]').hasClass('sdc-tab-active')).toBeTruthy(); + tabs.find('li[data-test-id="2"]').simulate('click'); + expect(tabs.instance().tabsInst.props.activeTab).toEqual('2'); + expect(tabs.find('li[data-test-id="2"]').hasClass('sdc-tab-active')).toBeTruthy(); + expect(tabs.find('li[data-test-id="1"]').hasClass('sdc-tab-active')).not.toBeTruthy(); + expect(tabs.find('.sdc-tab-content').text()).toEqual('Tab #2'); + }); + +}); diff --git a/test/react/Tile.spec.js b/test/react/Tile.spec.js new file mode 100644 index 0000000..7ce98a8 --- /dev/null +++ b/test/react/Tile.spec.js @@ -0,0 +1,30 @@ +import React from 'react'; +import Tile from '../../src/react/Tile.js'; +import TileInfo from '../../src/react/TileInfo.js'; +import TileInfoLine from '../../src/react/TileInfoLine.js'; +import TileFooter from '../../src/react/TileFooter.js'; +import TileFooterCell from '../../src/react/TileFooterCell.js'; + +import renderer from 'react-test-renderer'; + +describe('Tile', () => { + test('Empty tile', () => { + const tile = renderer.create().toJSON(); + expect(tile).toMatchSnapshot(); + }); + + test('Tile with props', () => { + const tile = renderer.create().toJSON(); + expect(tile).toMatchSnapshot(); + }); + + test('Tile with content info', () => { + const tile = renderer.create(Info).toJSON(); + expect(tile).toMatchSnapshot(); + }); + + test('Tile with footer', () => { + const tile = renderer.create(Footer).toJSON(); + expect(tile).toMatchSnapshot(); + }); +}); diff --git a/test/react/__snapshots__/Accordion.spec.js.snap b/test/react/__snapshots__/Accordion.spec.js.snap new file mode 100644 index 0000000..fe75ada --- /dev/null +++ b/test/react/__snapshots__/Accordion.spec.js.snap @@ -0,0 +1,32 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Accordion Accordion - Default 1`] = ` +
    +
    +
    + + +
    +
    + Accordion +
    +
    +
    + Accordion body +
    +
    +`; diff --git a/test/react/__snapshots__/Button.spec.js.snap b/test/react/__snapshots__/Button.spec.js.snap new file mode 100644 index 0000000..16e13bc --- /dev/null +++ b/test/react/__snapshots__/Button.spec.js.snap @@ -0,0 +1,163 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Button Button - Default - Gray 1`] = ` + +`; + +exports[`Button Button - Default - Negative 1`] = ` + +`; + +exports[`Button Button - Default - Positive 1`] = ` + +`; + +exports[`Button Button - Default - Primary - Disabled 1`] = ` + +`; + +exports[`Button Button - Default - Primary 1`] = ` + +`; + +exports[`Button Button - Default - Warning 1`] = ` + +`; + +exports[`Button Button - Default - White 1`] = ` + +`; + +exports[`Button Button - Link - Primary - Disabled 1`] = ` + +`; + +exports[`Button Button - Link - Primary - With Icon 1`] = ` + +`; + +exports[`Button Button - Link - Primary 1`] = ` + +`; + +exports[`Button Button - Outline - Gray 1`] = ` + +`; + +exports[`Button Button - Outline - Negative 1`] = ` + +`; + +exports[`Button Button - Outline - Positive 1`] = ` + +`; + +exports[`Button Button - Outline - Primary 1`] = ` + +`; diff --git a/test/react/__snapshots__/Checkbox.spec.js.snap b/test/react/__snapshots__/Checkbox.spec.js.snap new file mode 100644 index 0000000..fa6239b --- /dev/null +++ b/test/react/__snapshots__/Checkbox.spec.js.snap @@ -0,0 +1,49 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Checkbox Checkbox - disabled 1`] = ` +
    + +
    +`; + +exports[`Checkbox Checkbox - unchecked 1`] = ` +
    + +
    +`; diff --git a/test/react/__snapshots__/Checklist.spec.js.snap b/test/react/__snapshots__/Checklist.spec.js.snap new file mode 100644 index 0000000..707e910 --- /dev/null +++ b/test/react/__snapshots__/Checklist.spec.js.snap @@ -0,0 +1,165 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Checklist Checklist - Default 1`] = ` +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +`; + +exports[`Checklist Checklist - With disabled items 1`] = ` +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +`; diff --git a/test/react/__snapshots__/Input.spec.js.snap b/test/react/__snapshots__/Input.spec.js.snap new file mode 100644 index 0000000..62c3e2e --- /dev/null +++ b/test/react/__snapshots__/Input.spec.js.snap @@ -0,0 +1,179 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Input Input - default 1`] = ` +
    + + +
    +`; + +exports[`Input Input - error 1`] = ` +
    + + +
    +
    + + + this is an error + +
    +
    +
    +`; + +exports[`Input Input - number 1`] = ` +
    + + +
    +`; + +exports[`Input Input - placeholder 1`] = ` +
    + + +
    +`; + +exports[`Input Input - readonly 1`] = ` +
    + + +
    +`; + +exports[`Input Input - required 1`] = ` +
    + + +
    +`; diff --git a/test/react/__snapshots__/Modal.spec.js.snap b/test/react/__snapshots__/Modal.spec.js.snap new file mode 100644 index 0000000..c22da8d --- /dev/null +++ b/test/react/__snapshots__/Modal.spec.js.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Modal alert modal 1`] = `"
    Title
    Message
    "`; + +exports[`Modal custom modal 1`] = `"
    Title
    Message
    "`; + +exports[`Modal standard modal - not displayed 1`] = `"
    "`; + +exports[`Modal standard modal 1`] = `"
    Standard Modal
    Message
    "`; diff --git a/test/react/__snapshots__/ModalBody.spec.js.snap b/test/react/__snapshots__/ModalBody.spec.js.snap new file mode 100644 index 0000000..d0fda4d --- /dev/null +++ b/test/react/__snapshots__/ModalBody.spec.js.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ModalBody basic test 1`] = ` +
    +`; diff --git a/test/react/__snapshots__/ModalFooter.spec.js.snap b/test/react/__snapshots__/ModalFooter.spec.js.snap new file mode 100644 index 0000000..ee98355 --- /dev/null +++ b/test/react/__snapshots__/ModalFooter.spec.js.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ModalFooter basic test 1`] = ` +
    +
    + +
    +
    +`; diff --git a/test/react/__snapshots__/ModalHeader.spec.js.snap b/test/react/__snapshots__/ModalHeader.spec.js.snap new file mode 100644 index 0000000..8559925 --- /dev/null +++ b/test/react/__snapshots__/ModalHeader.spec.js.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ModalHeader basic test 1`] = ` +
    +
    + + +
    +
    + + +
    +
    +`; diff --git a/test/react/__snapshots__/ModalTitle.spec.js.snap b/test/react/__snapshots__/ModalTitle.spec.js.snap new file mode 100644 index 0000000..69a7734 --- /dev/null +++ b/test/react/__snapshots__/ModalTitle.spec.js.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ModalTitle basic test 1`] = ` +
    + Title +
    +`; diff --git a/test/react/__snapshots__/Panel.spec.js.snap b/test/react/__snapshots__/Panel.spec.js.snap new file mode 100644 index 0000000..b31dda7 --- /dev/null +++ b/test/react/__snapshots__/Panel.spec.js.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Panel Panel - Default 1`] = ` +
    + Panel +
    +`; diff --git a/test/react/__snapshots__/PopupMenu.spec.js.snap b/test/react/__snapshots__/PopupMenu.spec.js.snap new file mode 100644 index 0000000..7a9060f --- /dev/null +++ b/test/react/__snapshots__/PopupMenu.spec.js.snap @@ -0,0 +1,26 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PopupMenu check relative menu rendered 1`] = ` +
      +`; + +exports[`PopupMenu check separator rendered 1`] = ` +
    • +`; + +exports[`PopupMenu check static menu rendered 1`] = ` +
        +`; diff --git a/test/react/__snapshots__/PopupMenuItem.spec.js.snap b/test/react/__snapshots__/PopupMenuItem.spec.js.snap new file mode 100644 index 0000000..3334afb --- /dev/null +++ b/test/react/__snapshots__/PopupMenuItem.spec.js.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PopupMenuItem check disabled 1`] = ` +
      • + item 2 +
      • +`; + +exports[`PopupMenuItem check selected 1`] = ` +
      • + item 1 +
      • +`; diff --git a/test/react/__snapshots__/Radio.spec.js.snap b/test/react/__snapshots__/Radio.spec.js.snap new file mode 100644 index 0000000..a878d3a --- /dev/null +++ b/test/react/__snapshots__/Radio.spec.js.snap @@ -0,0 +1,49 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Radio Radio - disabled 1`] = ` +
        + +
        +`; + +exports[`Radio Radio - unchecked 1`] = ` +
        + +
        +`; diff --git a/test/react/__snapshots__/RadioGroup.spec.js.snap b/test/react/__snapshots__/RadioGroup.spec.js.snap new file mode 100644 index 0000000..caf729d --- /dev/null +++ b/test/react/__snapshots__/RadioGroup.spec.js.snap @@ -0,0 +1,60 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RadioGroup RadioGroup - basic rendering 1`] = ` +
        + +
        +
        + +
        +
        + +
        +
        +
        +`; diff --git a/test/react/__snapshots__/Tabs.spec.js.snap b/test/react/__snapshots__/Tabs.spec.js.snap new file mode 100644 index 0000000..9a4d0a1 --- /dev/null +++ b/test/react/__snapshots__/Tabs.spec.js.snap @@ -0,0 +1,52 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Tabs Tabs - basic rendering 1`] = ` +
        +
        +
        +
          +
        • + tab 1 +
        • +
        • + tab 2 +
        • +
        • + tab 3 +
        • +
        +
        + Tab #1 +
        +
        +
        +
        +`; diff --git a/test/react/__snapshots__/Tile.spec.js.snap b/test/react/__snapshots__/Tile.spec.js.snap new file mode 100644 index 0000000..e38ffa2 --- /dev/null +++ b/test/react/__snapshots__/Tile.spec.js.snap @@ -0,0 +1,108 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Tile Empty tile 1`] = ` +
        +
        +
        +
        +
        +
        +`; + +exports[`Tile Tile with content info 1`] = ` +
        +
        +
        +
        +
        +
        + Info +
        +
        +
        +
        +`; + +exports[`Tile Tile with footer 1`] = ` +
        +
        +
        +
        +
        +
        + + Footer + +
        +
        +`; + +exports[`Tile Tile with props 1`] = ` +
        +
        + header +
        +
        +
        +
        + + +
        +
        +
        +
        +`; diff --git a/test/react/utils/htmlLoader.js b/test/react/utils/htmlLoader.js new file mode 100644 index 0000000..fbdfbd8 --- /dev/null +++ b/test/react/utils/htmlLoader.js @@ -0,0 +1,7 @@ +const htmlLoader = require('html-loader'); + +module.exports = { + process(src) { + return htmlLoader(src); + } +}; diff --git a/test/react/utils/svgMock.js b/test/react/utils/svgMock.js new file mode 100644 index 0000000..2d6ed81 --- /dev/null +++ b/test/react/utils/svgMock.js @@ -0,0 +1,3 @@ +import React from 'react'; + +module.exports = () => ; \ No newline at end of file diff --git a/tsconfig.angular.build-es5.json b/tsconfig.angular.build-es5.json new file mode 100644 index 0000000..17e51af --- /dev/null +++ b/tsconfig.angular.build-es5.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "module": "es2015", + "target": "es5", + "lib": ["es2015", "dom"], + "skipLibCheck": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "sourceMap": true, + "moduleResolution": "node", + "declaration": true, + "outDir": "build", + "rootDir": "src" + }, + "files": [ + "src/angular/index.ts" + ], + "angularCompilerOptions": { + "skipMetadataEmit": false, + "strictMetadataEmit": false, + "skipTemplateCodegen": true, + "flatModuleId": "sdc-ui" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..040ef40 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "outDir": "dist", + "rootDir": ".", + "sourceMap": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "moduleResolution": "node", + "typeRoots": [ + "node_modules/@types" + ], + "lib": ["es6", "dom"] + }, + "exclude": [ + "node_modules" + ], + "types":[ + "jasmine" + ], + "awesomeTypescriptLoaderOptions": { + "useWebpackText": true + }, + "angularCompilerOptions": { + "debug": true + }, + "compileOnSave": false, + "buildOnSave": false, + "atom": { "rewriteTsconfig": false } +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..9aca897 --- /dev/null +++ b/tslint.json @@ -0,0 +1,77 @@ +{ + "extends": [ + "tslint:recommended", + "tslint-angular" + ], + "rulesDirectory": [ + "node_modules/codelyzer" + ], + "rules": { + "member-ordering": [ + false + ], + "angular-whitespace": [ + true, + "check-interpolation", + "check-pipe" + ], + "banana-in-box": true, + "templates-no-negated-async": true, + "directive-selector": [ + true, + "attribute", [ + "Sdc", + "Onap" + ], + "camelCase" + ], + "component-selector": [ + true, + "element", [ + "sdc", + "onap" + ], + "kebab-case" + ], + "use-input-property-decorator": true, + "use-output-property-decorator": true, + "use-host-property-decorator": true, + "no-attribute-parameter-decorator": true, + "no-input-rename": true, + "no-output-rename": true, + "no-forward-ref": true, + "use-view-encapsulation": false, + "use-life-cycle-interface": true, + "use-pipe-transform-interface": true, + "pipe-naming": [ + true, + "camelCase", + "Pipe" + ], + "component-class-suffix": [ + true, + "Component" + ], + "directive-class-suffix": [ + true, + "Directive" + ], + "templates-use-public": true, + "no-access-missing-member": true, + "invoke-injectable": true, + "template-to-ng-template": true, + "ordered-imports": [ + false + ], + "quotemark": [ + false + ], + "trailing-comma": [ + false + ], + "arrow-parens": false, + "object-literal-sort-keys": false, + "object-literal-shorthand": false, + "max-line-length": [true, 240, "^import |^export | implements "] + } +} diff --git a/utils/build-demo.js b/utils/build-demo.js new file mode 100644 index 0000000..8f5edda --- /dev/null +++ b/utils/build-demo.js @@ -0,0 +1,73 @@ +const ncp = require('ncp').ncp; +const fs = require('fs'); +const path = require('path') +const svgFolder = './assets/icons'; +const svgOutputFile = './src/style/scss/common/_icons.scss'; + +var copyFiles = function() { + // Copy generated style.css to demo/gen folder + ncp('lib/css/style.css', 'demo/gen', function (err) { + if (err) { + return console.error(err); + } + }); + + // Copy assets folder to demo/gen folder + ncp('assets', 'demo/gen/assets', function (err) { + if (err) { + return console.error(err); + } + }); +}; + +// var parseSvgContent = function(content) { +// var withoutLines = (content+'').replace(/\n|\t|\r/g, ''); +// return withoutLines.replace(/"/g,'\''); +// }; + +// var readFile = function(filePath) { +// return fs.readFileSync(filePath, 'utf8'); +// }; + +// var clearFile = function(filePath) { +// return fs.truncateSync(filePath, 0); +// }; + +// var writeFile = function(filePath){ +// const text = `.sdc-icon { +// display: inline-block; +// text-rendering: auto; +// -webkit-font-smoothing: antialiased; +// -moz-osx-font-smoothing: grayscale; +// width: 16px; +// height: 16px; +// }\n`; +// fs.writeFileSync(filePath, text); +// console.log("The file was saved!"); +// }; + +// var appendSvgIcons = function(content) { +// fs.appendFileSync(svgOutputFile, content); +// }; + +// var readFolder = function(folderPath) { +// var files = fs.readdirSync(svgFolder); +// files.forEach(file => { +// var extension = path.extname(file); +// if (extension ==='.svg'){ +// console.log(folderPath + "/" + file); +// var fileContent = readFile(folderPath + "/" + file); +// var svgContent = parseSvgContent(fileContent); +// var fileName = path.basename(file, '.svg') +// var scssContent = '.sdc-icon-' + fileName + ' {background-image: url("data:image/svg+xml;utf8,' + svgContent + '"); background-repeat: no-repeat;}'; +// appendSvgIcons(scssContent+'\n'); +// } +// }); + +// }; + +// Generate icons.scss file from multiple SVG files +// clearFile(svgOutputFile); +// writeFile(svgOutputFile); +// readFolder(svgFolder); +copyFiles(); diff --git a/utils/create-icon-map.js b/utils/create-icon-map.js new file mode 100644 index 0000000..d980895 --- /dev/null +++ b/utils/create-icon-map.js @@ -0,0 +1,26 @@ + +const fs = require('fs'); +const path = require('path'); + +const svgFolder = './assets/icons/'; +const iconMapFile = './src/react/utils/iconMap.js'; + +let dataToWrite = ''; +let iconNames = []; + +let iconMapDir = path.dirname(iconMapFile); +if (!fs.existsSync(iconMapDir)) { + fs.mkdirSync(iconMapDir); +}; + +fs.readdirSync(svgFolder).forEach(file => { + let fileName = file.split('.'); + if (fileName[0] && fileName[1] === 'svg') { + dataToWrite += `import ${fileName[0]} from '-!svg-react-loader!../../.${svgFolder}${file}';\n`; + iconNames.push(fileName[0]); + } +}); + +dataToWrite += '\n' + `export default {\n\t${iconNames.join(',\n\t')}\n};`; + +fs.writeFileSync(iconMapFile, dataToWrite); diff --git a/utils/create-svg-icons-map.js b/utils/create-svg-icons-map.js new file mode 100644 index 0000000..aec459c --- /dev/null +++ b/utils/create-svg-icons-map.js @@ -0,0 +1,95 @@ +const fs = require('fs'); +const path = require('path'); +const svgFolder = path.resolve(__dirname + '/../assets/sdc-icons/'); +const iconMapFile = path.resolve(__dirname + '/../src/common/icons-map.json'); +const iconMapTSFile = path.resolve(__dirname + '/../src/common/icons-map.ts'); +const disallowedSvgAttributes = ['fill', 'id', 'width', 'height']; +const disallowedSvgStyle = ['fill']; +const disallowedSvgInlineAttributes = ['fill', 'id']; +const disallowedSvgInlineStyle = ['fill']; + +function _escapeStrRegex(str) { + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} +function _makeSvgAttributesRegex(attrs) { + return new RegExp(`\s*(${attrs.map(_escapeStrRegex).join('|')})\s*=\s*("|')[^"']*\\2`, 'g'); +} +function _makeSvgStyleRegex(attrs) { + return new RegExp(`\s*${attrs.map(_escapeStrRegex).join('|')}\s*:[^'";]*;?`, 'g'); +} + +// prepare +const disallowedSvgAttributesRegex = _makeSvgAttributesRegex(disallowedSvgAttributes); +const disallowedSvgStyleRegex = _makeSvgStyleRegex(disallowedSvgStyle); +const disallowedSvgInlineAttributesRegex = _makeSvgAttributesRegex(disallowedSvgInlineAttributes); +const disallowedSvgInlineStyleRegex = _makeSvgStyleRegex(disallowedSvgInlineStyle); + +function addIcon(iconsObject, iconName, iconPath) { + let iconContent = fs.readFileSync(iconPath).toString(); + if (!iconContent) { + return; + } + + let iconInfoMsg = ''; + + // clean the first tag + iconContent = iconContent.replace(/]*>/, (svgTag) => { + let cleanedNum = 0; + const disallowedSvgAttributesMatch = svgTag.match(disallowedSvgAttributesRegex); + if (disallowedSvgAttributesMatch) { + svgTag = svgTag.replace(disallowedSvgAttributesRegex, ''); + cleanedNum += disallowedSvgAttributesMatch.length; + } + const disallowedSvgStyleMatch = svgTag.match(disallowedSvgStyleRegex); + if (disallowedSvgStyleMatch) { + svgTag = svgTag.replace(disallowedSvgStyleRegex, ''); + cleanedNum += disallowedSvgStyleMatch.length; + } + iconInfoMsg += 'ADDED'; + if (cleanedNum > 0) { + iconInfoMsg += `\n\t(cleaned ${cleanedNum} attributes and styles)`; + } + return svgTag; + }); + + const disallowedSvgInlineAttributesMatch = iconContent.match(disallowedSvgInlineAttributesRegex); + if (disallowedSvgInlineAttributesMatch) { + iconInfoMsg += `\n\t* CHECK for ${disallowedSvgInlineAttributesMatch.length} inline attributes [${disallowedSvgInlineAttributes.join(', ')}]`; + } + const disallowedSvgInlineStyleMatch = iconContent.match(disallowedSvgInlineStyleRegex); + if (disallowedSvgInlineStyleMatch) { + iconInfoMsg += `\n\t* CHECK for ${disallowedSvgInlineStyleMatch.length} inline styles [${disallowedSvgInlineStyle.join(', ')}]`; + } + + console.log(`# ${iconName}: ${iconInfoMsg}`); + + iconsObject[iconName] = iconContent; +} + +function main() { + const iconMapDir = path.dirname(iconMapFile); + if (!fs.existsSync(iconMapDir)) { + fs.mkdirSync(iconMapDir); + } + + const iconsObject = {}; + fs.readdirSync(svgFolder).forEach((file) => { + const fileName = file.split('.', 2)[0]; + const fileExtension = file.split('.', 2)[1]; + if (fileExtension === 'svg') { + const filePath = svgFolder + '/' + file; + if (fs.existsSync(filePath)) { + addIcon(iconsObject, fileName, filePath); + } + } + }); + + const dataToWrite = JSON.stringify(iconsObject); + + fs.writeFileSync(iconMapFile, dataToWrite); + fs.writeFileSync(iconMapTSFile, `export default ${dataToWrite};`); + + console.log(`Icons Map JSON created! [${iconMapFile}]`); +} + +main(); diff --git a/utils/index-for-gh-pages.html b/utils/index-for-gh-pages.html new file mode 100644 index 0000000..a6d7f63 --- /dev/null +++ b/utils/index-for-gh-pages.html @@ -0,0 +1,98 @@ + + + + + + + + Storybook + + + + + + + +
        + +
        +
        React
        +
        Angular
        +
        +
        + +
        + +
        + + + diff --git a/utils/main-page.html b/utils/main-page.html new file mode 100644 index 0000000..30fb10f --- /dev/null +++ b/utils/main-page.html @@ -0,0 +1,62 @@ + + + + + + + + Storybook + + + + + +
        Welcome to ONAP/UI project!
        +
        The aim of this project is to create a basic component library, with a unified UX and UI for both Angular and React based projects.
        + + + + \ No newline at end of file diff --git a/utils/scripts/map-sources.js b/utils/scripts/map-sources.js new file mode 100644 index 0000000..3ce6bfb --- /dev/null +++ b/utils/scripts/map-sources.js @@ -0,0 +1,9 @@ +const sorcery = require('sorcery'); + +const argv = require('yargs') + .alias('f', 'file') + .argv; + +sorcery.load(argv.file).then(function (chain) { + chain.write(); +}); diff --git a/version.properties b/version.properties new file mode 100644 index 0000000..b479afe --- /dev/null +++ b/version.properties @@ -0,0 +1,13 @@ +########################################################### +# Versioning variables +# Note that these variables cannot be structured (e.g. : version.release or version.snapshot etc... ) +# because they are used in Jenkins, whose plug-in doesn't support + +major=1 +minor=2 +patch=0 + +base_version=${major}.${minor}.${patch} + +release_version=${base_version} +snapshot_version=${base_version}-SNAPSHOT \ No newline at end of file diff --git a/webpack.build.config.js b/webpack.build.config.js new file mode 100644 index 0000000..9c74402 --- /dev/null +++ b/webpack.build.config.js @@ -0,0 +1,63 @@ +var path = require('path'); + +var webpackConfig = { + + devtool: 'source-map', + + entry: {}, + + output: { + publicPath: '' + }, + + plugins: [ + ], + + module: { + rules: [ + // .ts files for TypeScript + { + test: /.ts$/, + use: [ + 'awesome-typescript-loader', + 'angular2-template-loader', + 'angular2-router-loader' + ] + }, + { + test: /\.html$/, + loader: 'html-loader' + }, + { + test: /\.scss$/, + use: ['style-loader', 'css-loader', 'sass-loader'], + include: path.resolve(__dirname, '../') + }, + { + test: /\.json$/, + loader: 'json', + include: path.resolve(__dirname, '../') + + }, + { + test: /\.svg$/, + use: 'svg-sprite-loader', + options: { + extract: true, + + } + } + ] + }, + + resolve: { + extensions: ['.ts', '.js', '.json'], + modules: [path.resolve(__dirname, 'node_modules')], + alias: { + root: path.resolve(__dirname), + app: path.resolve(__dirname, 'src') + } + } +}; + +module.exports = webpackConfig; diff --git a/webpack/helpers.js b/webpack/helpers.js new file mode 100644 index 0000000..c2c3717 --- /dev/null +++ b/webpack/helpers.js @@ -0,0 +1,10 @@ +var path = require('path'); + +var _root = path.resolve(__dirname, '..'); + +function root(args) { + args = Array.prototype.slice.call(arguments, 0); + return path.join.apply(path, [_root].concat(args)); +} + +exports.root = root; diff --git a/webpack/webpack.test.js b/webpack/webpack.test.js new file mode 100644 index 0000000..9057d4e --- /dev/null +++ b/webpack/webpack.test.js @@ -0,0 +1,71 @@ +/**************************************************************************************** + * Webpack test configuration file + * Using the current configuration we are able to bundle all the files we need for our + * unit testings. + ***************************************************************************************/ + + +'use strict'; + +/** + * Dependencies: + * path: node library for resolving full path names + * webpack: for use in our testings + */ +const path = require('path'); +const webpack = require('webpack'); +const helpers = require('./helpers'); +module.exports = { + /** + * Choose a developer tool to enhance debugging. inline-source-map - A SourceMap is added as DataUrl to the JavaScript file. + */ + devtool: 'inline-source-map', + + /** + * Set webpack loaders and preloaders for typescript, angular2, SASS and HTML, + * so that webpack will be able to process and bundle them. + */ + module: { + + rules: [ + { + test: /.ts$/, + exclude: /node_modules/, + use: [ + 'awesome-typescript-loader', + 'angular2-template-loader', + 'angular2-router-loader' + ] + }, + { test: /\.html$/, loader: "html-loader" }, + { + test: /.scss$/, + use: ['style-loader', 'css-loader', 'sass-loader'], + include: path.resolve(__dirname, '../') + }, + + ] + }, + + plugins: [ + // Workaround for angular/angular#11580 + new webpack.ContextReplacementPlugin( + // The (\\|\/) piece accounts for path separators in *nix and Windows + ///angular(\\|\/)core(\\|\/)@angular/, + /@angular(\\|\/)core(\\|\/)/, + helpers.root('./src'), // location of your src + {} // a map of your routes + ), + ], + + /** + * This section lets Webpack know which types of file extensions it should be loading. + */ + resolve: { + extensions: ['.js', '.ts'], + modules: [ + path.resolve('.', 'src'), + 'node_modules' + ] + }, +}; -- cgit 1.2.3-korg