From d33d387158957a84fd399a60ee50cf07a3286d5f Mon Sep 17 00:00:00 2001 From: webviewer-ui <72055195+webviewer-ui@users.noreply.github.com> Date: Wed, 5 Jun 2024 09:42:18 -0700 Subject: [PATCH] =?UTF-8?q?[Bugfix]=20Modular=20UI=20-=20Fixing=20Custom?= =?UTF-8?q?=20and=20Toggle=20button=20styles=20(#9067)=20(master=20?= =?UTF-8?q?=E2=86=92=20master)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .storybook/main.js | 40 ++++++-- package.json | 3 +- .../AppStories/App.stories.js | 99 +++++++++---------- .../CustomButton/CustomButton.scss | 57 +++++++++++ .../CustomButton/CustomButton.stories.js | 72 ++++++++++++++ .../ToggleElementButton.scss | 10 +- .../ToggleElementButton.stories.js | 17 +++- 7 files changed, 232 insertions(+), 66 deletions(-) create mode 100644 src/components/ModularComponents/CustomButton/CustomButton.scss create mode 100644 src/components/ModularComponents/CustomButton/CustomButton.stories.js diff --git a/.storybook/main.js b/.storybook/main.js index fbcd8d2f2..4887d385b 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -3,15 +3,19 @@ const path = require('path'); const appDirectory = path.join(__dirname, '..'); module.exports = { - stories: [ - "../src/**/*.stories.mdx", - "../src/**/*.stories.@(js|jsx|ts|tsx)" - ], + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], staticDirs: ['./static'], + addons: [ "@storybook/addon-links", - "@storybook/addon-essentials" + "@storybook/addon-essentials", + '@storybook/addon-webpack5-compiler-swc', + '@chromatic-com/storybook', + '@storybook/addon-interactions', + '@storybook/addon-a11y', + 'storybook-addon-pseudo-states' ], + webpackFinal: async (config, { configType }) => { // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION' // You can change the configuration based on that. @@ -72,5 +76,29 @@ module.exports = { return config; }, - framework: '@storybook/react', + + framework: { + name: '@storybook/react-webpack5', + options: {} + }, + + docs: {}, + + swcLoaderOptions: { + jsc: { + parser: { + syntax: 'ecmascript', + jsx: true, + }, + transform: { + react: { + runtime: 'automatic' + } + } + } + }, + + typescript: { + reactDocgen: 'react-docgen-typescript' + } } diff --git a/package.json b/package.json index 8f483576d..08d0b8860 100644 --- a/package.json +++ b/package.json @@ -130,6 +130,7 @@ "sass": "^1.34.0", "sass-loader": "^8.0.2", "storybook": "^8.1.0", + "storybook-addon-pseudo-states": "^3.1.1", "style-loader": "^1.1.3", "svg-inline-loader": "^0.8.0", "typescript": "^5.4.5", @@ -139,4 +140,4 @@ "webpack-dev-middleware": "^7.2.1", "webpack-hot-middleware": "^2.26.1" } -} \ No newline at end of file +} diff --git a/src/components/ModularComponents/AppStories/App.stories.js b/src/components/ModularComponents/AppStories/App.stories.js index 66044b485..09526fff1 100644 --- a/src/components/ModularComponents/AppStories/App.stories.js +++ b/src/components/ModularComponents/AppStories/App.stories.js @@ -1,67 +1,62 @@ -import React from 'react'; -import { Provider } from 'react-redux'; -import { configureStore } from '@reduxjs/toolkit'; import App from 'components/App'; -import initialState from 'src/redux/initialState'; -import rootReducer from 'reducers/rootReducer'; import { mockHeadersNormalized, mockModularComponents } from './mockAppState'; +import { userEvent, within, expect } from '@storybook/test'; +import { createTemplate } from 'helpers/storybookHelper'; export default { title: 'ModularComponents/App', component: App, + includeStories: ['DefaultUI', 'ActiveGroupHeaderTest', 'HeaderButtonsWithLabelsAndIcons'], + excludeStories: ['CreateTemplate'], }; -const noop = () => { }; +export const DefaultUI = createTemplate({ headers: mockHeadersNormalized, components: mockModularComponents }); -const MockApp = ({ initialState }) => { - return ( - getDefaultMiddleware({ serializableCheck: false }) - })}> - - - ); +export const ActiveGroupHeaderTest = createTemplate({ headers: mockHeadersNormalized, components: mockModularComponents }); +ActiveGroupHeaderTest.play = async ({ canvasElement }) => { + const canvas = within(canvasElement); + // We should be able to find the underline button since the default selected group is annotateGroupedItems + await canvas.findAllByRole('button', { name: 'Underline' }); + // Now if we click on ribbon + const viewRibbon = await canvas.findByLabelText('View'); + await userEvent.click(viewRibbon); + // the underline button should not be visible anymore as the header is hidden + expect(await canvas.queryByRole('button', { name: 'Underline' })).toBeNull(); }; -const Template = (args) => { - const stateWithHeaders = { - ...initialState, - viewer: { - ...initialState.viewer, - modularHeaders: args.headers, - modularComponents: args.components, - openElements: {}, - genericPanels: [{ - dataElement: 'stylePanel', - render: 'stylePanel', - location: 'left', - }], - activeGroupedItems: ['annotateGroupedItems'], - lastPickedToolForGroupedItems: { - annotateGroupedItems: 'AnnotationCreateTextUnderline', - }, - activeCustomRibbon: 'annotations-ribbon-item', - lastPickedToolAndGroup: { - tool: 'AnnotationCreateTextUnderline', - group: ['annotateGroupedItems'], - }, - activeToolName: 'AnnotationCreateTextUnderline' - }, - featureFlags: { - customizableUI: true, - }, - }; - return ; +const customHeaders = { + ...mockHeadersNormalized, + 'default-top-header': { + ...mockHeadersNormalized['default-top-header'], + 'items': [ + ...mockHeadersNormalized['default-top-header'].items, + 'labelButton', + 'labelAndIconButton' + ] + } }; -function createTemplate({ headers, components }) { - const template = Template.bind({}); - template.args = { headers, components }; - template.parameters = { layout: 'fullscreen' }; - return template; -} +const labelAndIconButton = { + 'dataElement': 'labelAndIconButton', + 'title': 'Export button', + 'type': 'customButton', + 'label': 'Export', + 'img': 'icon-save', + 'onClick': () => {}, +}; -export const DefaultUI = createTemplate({ headers: mockHeadersNormalized, components: mockModularComponents }); +const labelButton = { + 'dataElement': 'labelButton', + 'title': 'Toggle the visibility of Flyout', + 'type': 'toggleButton', + 'label': 'Custom Flyout', + 'toggleElement': 'myCustomFlyout' +}; + +const customComponents = { + ...mockModularComponents, + 'labelAndIconButton': labelAndIconButton, + 'labelButton': labelButton +}; +export const HeaderButtonsWithLabelsAndIcons = createTemplate({ headers: customHeaders, components: customComponents }); \ No newline at end of file diff --git a/src/components/ModularComponents/CustomButton/CustomButton.scss b/src/components/ModularComponents/CustomButton/CustomButton.scss new file mode 100644 index 000000000..e8ea021c1 --- /dev/null +++ b/src/components/ModularComponents/CustomButton/CustomButton.scss @@ -0,0 +1,57 @@ +@import '../../../constants/styles'; + +.CustomButton { + padding: 5px; + width: fit-content; + min-width: 32px; + + &:hover, + &:active { + @extend %custom-button-hover; + } + + &:active { + color: var(--view-header-icon-active-fill); + cursor: default; + + .Icon svg path { + fill: var(--view-header-icon-active-fill) + } + } +} + +.confirm-button { + background-color: var(--primary-button); + border: 1px solid var(--primary-button); + color: var(--primary-button-text); + padding: 7px 14px; + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; + border-radius: 5px; + height: 30px; + cursor: pointer; + + &:hover { + background: var(--primary-button-hover) !important; + border: 1px solid var(--primary-button-hover) !important; + border-radius: 5px !important; + } +} + +.cancel-button { + color: var(--secondary-button-text); + background-color: transparent; + padding: 7px 14px; + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; + border-radius: 5px; + height: 30px; + cursor: pointer; + + &:hover { + color: var(--secondary-button-hover); + background: transparent; + } +} \ No newline at end of file diff --git a/src/components/ModularComponents/CustomButton/CustomButton.stories.js b/src/components/ModularComponents/CustomButton/CustomButton.stories.js new file mode 100644 index 000000000..8533a40e7 --- /dev/null +++ b/src/components/ModularComponents/CustomButton/CustomButton.stories.js @@ -0,0 +1,72 @@ +import React from 'react'; +import CustomButton from './CustomButton'; +import initialState from 'src/redux/initialState'; +import { Provider } from 'react-redux'; +import { configureStore } from '@reduxjs/toolkit'; + +export default { + title: 'Components/CustomButton', + component: CustomButton, +}; + +const store = configureStore({ + reducer: () => initialState +}); + +const BasicComponent = (props) => { + return ( + + + + ); +}; + +export const DefaultButton = BasicComponent.bind({}); +DefaultButton.args = { + dataElement: 'button-data-element', + title: 'Button title', + disabled: false, + label: 'Click', + img: 'icon-save', + onClick: () => { + alert('Clicked!'); + } +}; + +export const DefaultButtonOnHover = BasicComponent.bind({}); +DefaultButtonOnHover.args = { + dataElement: 'button-data-element', + title: 'Button title', + disabled: false, + label: 'Click', + img: 'icon-save', + onClick: () => { + alert('Clicked!'); + } +}; + +DefaultButtonOnHover.parameters = { + pseudo: { hover: true }, +}; + +export const ConfirmButton = BasicComponent.bind({}); +ConfirmButton.args = { + dataElement: 'button-data-element', + title: 'Apply Fields', + label: 'Apply Fields', + preset: 'confirm', + onClick: () => { + alert('Apply Fields button clicked!'); + } +}; + +export const CancelButton = BasicComponent.bind({}); +CancelButton.args = { + dataElement: 'button-data-element', + title: 'Cancel', + label: 'Cancel', + preset: 'cancel', + onClick: () => { + alert('Cancel button clicked!'); + } +}; \ No newline at end of file diff --git a/src/components/ModularComponents/ToggleElementButton/ToggleElementButton.scss b/src/components/ModularComponents/ToggleElementButton/ToggleElementButton.scss index a17071a88..cddbf9f8d 100644 --- a/src/components/ModularComponents/ToggleElementButton/ToggleElementButton.scss +++ b/src/components/ModularComponents/ToggleElementButton/ToggleElementButton.scss @@ -2,15 +2,17 @@ .ToggleElementButton { .Button { - width: 32px; + width: auto; + min-width: 32px; height: 32px; + padding: 5px; - &:hover { - background: var(--tools-button-hover); + &:hover, + &.active { + @extend %custom-button-hover; } &.active { - background: var(--tools-button-active); color: var(--view-header-icon-active-fill); cursor: default; diff --git a/src/components/ModularComponents/ToggleElementButton/ToggleElementButton.stories.js b/src/components/ModularComponents/ToggleElementButton/ToggleElementButton.stories.js index 7bad08a54..ee64334de 100644 --- a/src/components/ModularComponents/ToggleElementButton/ToggleElementButton.stories.js +++ b/src/components/ModularComponents/ToggleElementButton/ToggleElementButton.stories.js @@ -6,6 +6,9 @@ import { Provider } from 'react-redux'; export default { title: 'ModularComponents/ToggleElementButton', component: ToggleElementButton, + parameters: { + customizableUI: true, + }, }; const initialState = { @@ -44,8 +47,16 @@ const store = configureStore({ export const ToggleElementButtonComponent = () => ( -
- -
+
); + +export const ToggleElementButtonWithLabelOnHoverState = () => ( + + + +); + +ToggleElementButtonWithLabelOnHoverState.parameters = { + pseudo: { hover: true }, +};