From 58a8c4bfb11a18f998b96538a3c39fbbe1ec2e3b Mon Sep 17 00:00:00 2001 From: Seth Foster Date: Wed, 11 Mar 2020 12:38:27 -0400 Subject: [PATCH 1/4] fix(app): disable module commands when protocol paused The module command cards displayed in the protocol run page have historically allowed users to send module commands (like setting temperature) when the protocol was active but paused. That used to work essentially as a side effect: modules didn't respect pausing, so you could tell them to do things while the protocol was paused. Now that modules respect pausing, sending module commands when the protocol is paused results in the modules not doing anything. The science and UX intent of controlling modules from the protocol run page is to implement pre-protocol preparation (like preheat) or post-protocol behavior (like keeping some incubation going), not to control the modules when paused - and in fact, when more advanced module control behaviors land in protocol designer or in the python api, controlling modules when the protocol is paused could break protocol execution. This PR changes the module command cards to only work when the protocol is not paused or running. --- app/src/components/ModuleLiveStatusCards/TempDeckCard.js | 6 +++--- .../components/ModuleLiveStatusCards/ThermocyclerCard.js | 6 +++--- app/src/components/ModuleLiveStatusCards/index.js | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/components/ModuleLiveStatusCards/TempDeckCard.js b/app/src/components/ModuleLiveStatusCards/TempDeckCard.js index 6ba892a5cd1..66792ab6d0b 100644 --- a/app/src/components/ModuleLiveStatusCards/TempDeckCard.js +++ b/app/src/components/ModuleLiveStatusCards/TempDeckCard.js @@ -17,7 +17,7 @@ type Props = {| command: ModuleCommand, args?: Array ) => mixed, - isProtocolActive: boolean, + allowInteraction: boolean, isCardExpanded: boolean, toggleCard: boolean => mixed, |} @@ -25,7 +25,7 @@ type Props = {| export const TempDeckCard = ({ module, sendModuleCommand, - isProtocolActive, + allowInteraction, isCardExpanded, toggleCard, }: Props) => ( @@ -36,7 +36,7 @@ export const TempDeckCard = ({ >
- {!isProtocolActive && ( + {allowInteraction && ( ) => mixed, - isProtocolActive: boolean, + allowInteraction: boolean, isCardExpanded: boolean, toggleCard: boolean => mixed, |} @@ -84,7 +84,7 @@ type Props = {| export const ThermocyclerCard = ({ module, sendModuleCommand, - isProtocolActive, + allowInteraction, isCardExpanded, toggleCard, }: Props) => { @@ -113,7 +113,7 @@ export const ThermocyclerCard = ({ >
- {!isProtocolActive && ( + {allowInteraction && ( { const modules = useSelector(getAttachedModulesForConnectedRobot) const sendModuleCommand = useSendModuleCommand() - const isProtocolActive: boolean = useSelector(robotSelectors.getIsActive) + const isProtocolRunning: boolean = useSelector(robotSelectors.getIsRunning) const [expandedCard, setExpandedCard] = React.useState( modules.length > 0 ? modules[0].serial : '' ) @@ -47,7 +47,7 @@ export const ModuleLiveStatusCards = () => { toggleCard={makeToggleCard(module.serial)} isCardExpanded={expandedCard === module.serial} sendModuleCommand={sendModuleCommand} - isProtocolActive={isProtocolActive} + allowInteraction={!isProtocolRunning} /> ) case THERMOCYCLER: @@ -58,7 +58,7 @@ export const ModuleLiveStatusCards = () => { toggleCard={makeToggleCard(module.serial)} isCardExpanded={expandedCard === module.serial} sendModuleCommand={sendModuleCommand} - isProtocolActive={isProtocolActive} + allowInteraction={!isProtocolRunning} /> ) case MAGDECK: From 7cbb8fb42e49f9b9ba4320e4ba520aca07691d82 Mon Sep 17 00:00:00 2001 From: Seth Foster Date: Thu, 12 Mar 2020 11:05:40 -0400 Subject: [PATCH 2/4] fixup: remove unused selectors --- app/src/robot/selectors.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/src/robot/selectors.js b/app/src/robot/selectors.js index c2caba01bc5..f6219c3f9dd 100644 --- a/app/src/robot/selectors.js +++ b/app/src/robot/selectors.js @@ -95,10 +95,6 @@ export function getIsReadyToRun(state: State): boolean { return getSessionStatus(state) === ('loaded': SessionStatus) } -export function getIsActive(state: State): boolean { - return getSessionStatus(state) === ('running': SessionStatus) -} - export function getIsRunning(state: State): boolean { const status = getSessionStatus(state) From ec70881195ec390ec8c31077fc6348f9e77a4cf5 Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Mon, 16 Mar 2020 12:36:20 -0400 Subject: [PATCH 3/4] fixup: Ensure all instances of module control are disabled the same way --- .../InstrumentSettings/AttachedModulesCard.js | 15 +- .../InstrumentSettings/ModulesCardContents.js | 10 +- .../ModuleControls/TemperatureControl.js | 9 +- app/src/components/ModuleControls/index.js | 6 +- app/src/components/ModuleItem/ModuleUpdate.js | 13 +- .../ModuleItem/__tests__/ModuleUpdate.test.js | 123 +++++++------ .../__snapshots__/ModuleUpdate.test.js.snap | 173 ------------------ app/src/components/ModuleItem/index.js | 11 +- .../ModuleLiveStatusCards/TempDeckCard.js | 15 +- .../ModuleLiveStatusCards/ThermocyclerCard.js | 15 +- .../components/ModuleLiveStatusCards/index.js | 9 +- app/src/modules/__tests__/selectors.test.js | 98 ++++++++++ app/src/modules/i18n.js | 7 + app/src/modules/selectors.js | 22 +++ 14 files changed, 251 insertions(+), 275 deletions(-) delete mode 100644 app/src/components/ModuleItem/__tests__/__snapshots__/ModuleUpdate.test.js.snap create mode 100644 app/src/modules/i18n.js diff --git a/app/src/components/InstrumentSettings/AttachedModulesCard.js b/app/src/components/InstrumentSettings/AttachedModulesCard.js index 5a393414cce..632e9e1db28 100644 --- a/app/src/components/InstrumentSettings/AttachedModulesCard.js +++ b/app/src/components/InstrumentSettings/AttachedModulesCard.js @@ -4,7 +4,11 @@ import * as React from 'react' import { useDispatch, useSelector } from 'react-redux' import { Card, useInterval } from '@opentrons/components' -import { fetchModules, getAttachedModules } from '../../modules' +import { + fetchModules, + getAttachedModules, + getModuleControlsDisabled, +} from '../../modules' import { getConnectedRobotName } from '../../robot/selectors' import { ModulesCardContents } from './ModulesCardContents' @@ -22,7 +26,9 @@ export function AttachedModulesCard(props: Props) { const modules = useSelector((state: State) => getAttachedModules(state, robotName) ) - const canControl = connectedRobotName === robotName + const controlDisabledReason = useSelector((state: State) => + getModuleControlsDisabled(state, robotName) + ) // if robot is connected, the modules epic will poll /modules automatically, // but we need to poll ourselves if we're viewing this robot without @@ -35,7 +41,10 @@ export function AttachedModulesCard(props: Props) { return ( - + ) } diff --git a/app/src/components/InstrumentSettings/ModulesCardContents.js b/app/src/components/InstrumentSettings/ModulesCardContents.js index 7427aae6505..f0e7dbc8094 100644 --- a/app/src/components/InstrumentSettings/ModulesCardContents.js +++ b/app/src/components/InstrumentSettings/ModulesCardContents.js @@ -7,17 +7,21 @@ import type { AttachedModule } from '../../modules/types' type Props = {| modules: Array, - canControl: boolean, + controlDisabledReason: string | null, |} export function ModulesCardContents(props: Props) { - const { modules, canControl } = props + const { modules, controlDisabledReason } = props if (modules.length === 0) return return ( <> {modules.map(mod => ( - + ))} ) diff --git a/app/src/components/ModuleControls/TemperatureControl.js b/app/src/components/ModuleControls/TemperatureControl.js index a1d751b8936..47ef4f04079 100644 --- a/app/src/components/ModuleControls/TemperatureControl.js +++ b/app/src/components/ModuleControls/TemperatureControl.js @@ -18,7 +18,6 @@ import type { import { THERMOCYCLER } from '../../modules' import { getModuleDisplayName } from '@opentrons/shared-data' -const CONNECT_FOR_CONTROL = 'Connect to robot to control modules' type Props = {| module: ThermocyclerModule | TemperatureModule, sendModuleCommand: ( @@ -26,13 +25,13 @@ type Props = {| command: ModuleCommand, args?: Array ) => mixed, - disabled?: boolean, + disabledReason?: string | null, |} export const TemperatureControl = ({ module, sendModuleCommand, - disabled, + disabledReason, }: Props) => { const [primaryTempValue, setPrimaryTempValue] = useState(null) const [secondaryTempValue, setSecondaryTempValue] = useState(null) @@ -125,12 +124,12 @@ export const TemperatureControl = ({ )} - + {hoverTooltipHandlers => (
{hasTarget === true ? 'Deactivate' : 'Set Temp'} diff --git a/app/src/components/ModuleControls/index.js b/app/src/components/ModuleControls/index.js index b280e40d49d..d87401e4b49 100644 --- a/app/src/components/ModuleControls/index.js +++ b/app/src/components/ModuleControls/index.js @@ -10,11 +10,11 @@ import type { TemperatureModule, ThermocyclerModule } from '../../modules/types' type Props = {| module: TemperatureModule | ThermocyclerModule, - canControl: boolean, + controlDisabledReason: string | null, |} export function ModuleControls(props: Props) { - const { module: mod, canControl } = props + const { module: mod, controlDisabledReason } = props const sendModuleCommand = useSendModuleCommand() return ( @@ -41,7 +41,7 @@ export function ModuleControls(props: Props) {
diff --git a/app/src/components/ModuleItem/ModuleUpdate.js b/app/src/components/ModuleItem/ModuleUpdate.js index 9733035e838..06f5007cd3e 100644 --- a/app/src/components/ModuleItem/ModuleUpdate.js +++ b/app/src/components/ModuleItem/ModuleUpdate.js @@ -24,7 +24,6 @@ import type { RequestState } from '../../robot-api/types' import styles from './styles.css' const FW_IS_UP_TO_DATE = 'Module Firmware is up to date' -const CONNECT_TO_UPDATE = 'Connect to Robot to update Module' const OK_TEXT = 'Ok' const UPDATE = 'update' @@ -36,12 +35,12 @@ const FAILED_UPDATE_BODY = type Props = {| hasAvailableUpdate: boolean, - canControl: boolean, + controlDisabledReason: string | null, moduleId: string, |} export function ModuleUpdate(props: Props) { - const { hasAvailableUpdate, moduleId, canControl } = props + const { hasAvailableUpdate, moduleId, controlDisabledReason } = props const dispatch = useDispatch() const robotName = useSelector(getConnectedRobotName) const [ @@ -49,6 +48,7 @@ export function ModuleUpdate(props: Props) { requestIds, ] = useDispatchApiRequest() + const canControl = controlDisabledReason === null const handleClick = () => { canControl && robotName && @@ -62,8 +62,11 @@ export function ModuleUpdate(props: Props) { const buttonText = hasAvailableUpdate ? UPDATE : UPDATE_TO_DATE let tooltipText = null - if (!canControl) tooltipText = CONNECT_TO_UPDATE - if (!hasAvailableUpdate) tooltipText = FW_IS_UP_TO_DATE + if (!hasAvailableUpdate) { + tooltipText = FW_IS_UP_TO_DATE + } else if (controlDisabledReason !== null) { + tooltipText = controlDisabledReason + } const handleCloseErrorModal = () => { dispatch(dismissRequest(latestRequestId)) diff --git a/app/src/components/ModuleItem/__tests__/ModuleUpdate.test.js b/app/src/components/ModuleItem/__tests__/ModuleUpdate.test.js index 4e4d8e92d70..96b0fd30e9e 100644 --- a/app/src/components/ModuleItem/__tests__/ModuleUpdate.test.js +++ b/app/src/components/ModuleItem/__tests__/ModuleUpdate.test.js @@ -1,7 +1,7 @@ // @flow import * as React from 'react' import { Provider } from 'react-redux' -import { shallow, mount } from 'enzyme' +import { mount } from 'enzyme' import configureMockStore from 'redux-mock-store' import { Button } from '@opentrons/components' @@ -11,6 +11,7 @@ const mockStore = configureMockStore([]) describe('ModuleUpdate', () => { let store + beforeEach(() => { store = mockStore({ mockState: true, @@ -25,84 +26,88 @@ describe('ModuleUpdate', () => { jest.resetAllMocks() }) - it('component renders', () => { - const treeAvailableCanControl = shallow( - - - - ) - const treeAvailableNoControl = shallow( - - - - ) - const treeNotAvailableCanControl = shallow( - - - - ) - const treeNotAvailableNoControl = shallow( - - - - ) - expect(treeAvailableCanControl).toMatchSnapshot() - expect(treeNotAvailableCanControl).toMatchSnapshot() - expect(treeAvailableNoControl).toMatchSnapshot() - expect(treeNotAvailableNoControl).toMatchSnapshot() - }) + // TODO(mc, 2020-03-16): these shallow snapshots don't test anything + // remove commented out tests when actual tests have been written + // it('component renders', () => { + // const treeAvailableCanControl = shallow( + // + // + // , + // ) + // const treeAvailableNoControl = shallow( + // + // + // + // ) + // const treeNotAvailableCanControl = shallow( + // + // + // + // ) + // const treeNotAvailableNoControl = shallow( + // + // + // + // ) + // expect(treeAvailableCanControl).toMatchSnapshot() + // expect(treeNotAvailableCanControl).toMatchSnapshot() + // expect(treeAvailableNoControl).toMatchSnapshot() + // expect(treeNotAvailableNoControl).toMatchSnapshot() + // }) it('displays a Warning for invalid files', () => { const SPECS = [ { - canControl: true, + controlDisabledReason: null, hasAvailableUpdate: true, expectDisabled: false, }, { - canControl: true, + controlDisabledReason: null, hasAvailableUpdate: false, expectDisabled: true, }, { - canControl: false, + controlDisabledReason: "Can't touch this", hasAvailableUpdate: true, expectDisabled: true, }, { - canControl: false, + controlDisabledReason: "Can't touch this", hasAvailableUpdate: false, expectDisabled: true, }, ] - SPECS.forEach(({ canControl, hasAvailableUpdate, expectDisabled }) => { - const wrapper = mount( - - - - ) - expect(wrapper.find(Button).prop('disabled')).toEqual(expectDisabled) - }) + SPECS.forEach( + ({ controlDisabledReason, hasAvailableUpdate, expectDisabled }) => { + const wrapper = mount( + + + + ) + expect(wrapper.find(Button).prop('disabled')).toEqual(expectDisabled) + } + ) }) }) diff --git a/app/src/components/ModuleItem/__tests__/__snapshots__/ModuleUpdate.test.js.snap b/app/src/components/ModuleItem/__tests__/__snapshots__/ModuleUpdate.test.js.snap deleted file mode 100644 index 9767ff9aba6..00000000000 --- a/app/src/components/ModuleItem/__tests__/__snapshots__/ModuleUpdate.test.js.snap +++ /dev/null @@ -1,173 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ModuleUpdate component renders 1`] = ` - - - -`; - -exports[`ModuleUpdate component renders 2`] = ` - - - -`; - -exports[`ModuleUpdate component renders 3`] = ` - - - -`; - -exports[`ModuleUpdate component renders 4`] = ` - - - -`; diff --git a/app/src/components/ModuleItem/index.js b/app/src/components/ModuleItem/index.js index 7ae64847d52..5f1931a0a04 100644 --- a/app/src/components/ModuleItem/index.js +++ b/app/src/components/ModuleItem/index.js @@ -11,12 +11,12 @@ import { TEMPDECK, THERMOCYCLER } from '../../modules' type Props = {| module: AttachedModule, - canControl: boolean, + controlDisabledReason: string | null, availableUpdate?: ?string, |} export function ModuleItem(props: Props) { - const { module, canControl } = props + const { module, controlDisabledReason } = props return (
@@ -25,12 +25,15 @@ export function ModuleItem(props: Props) {
{(module.name === THERMOCYCLER || module.name === TEMPDECK) && ( - + )}
) diff --git a/app/src/components/ModuleLiveStatusCards/TempDeckCard.js b/app/src/components/ModuleLiveStatusCards/TempDeckCard.js index 66792ab6d0b..e59cf2a06b0 100644 --- a/app/src/components/ModuleLiveStatusCards/TempDeckCard.js +++ b/app/src/components/ModuleLiveStatusCards/TempDeckCard.js @@ -17,7 +17,7 @@ type Props = {| command: ModuleCommand, args?: Array ) => mixed, - allowInteraction: boolean, + controlDisabledReason: string | null, isCardExpanded: boolean, toggleCard: boolean => mixed, |} @@ -25,7 +25,7 @@ type Props = {| export const TempDeckCard = ({ module, sendModuleCommand, - allowInteraction, + controlDisabledReason, isCardExpanded, toggleCard, }: Props) => ( @@ -36,12 +36,11 @@ export const TempDeckCard = ({ >
- {allowInteraction && ( - - )} +
) => mixed, - allowInteraction: boolean, + controlDisabledReason: string | null, isCardExpanded: boolean, toggleCard: boolean => mixed, |} @@ -84,7 +84,7 @@ type Props = {| export const ThermocyclerCard = ({ module, sendModuleCommand, - allowInteraction, + controlDisabledReason, isCardExpanded, toggleCard, }: Props) => { @@ -113,12 +113,11 @@ export const ThermocyclerCard = ({ >
- {allowInteraction && ( - - )} +
{ const modules = useSelector(getAttachedModulesForConnectedRobot) const sendModuleCommand = useSendModuleCommand() - const isProtocolRunning: boolean = useSelector(robotSelectors.getIsRunning) + const controlDisabledReason = useSelector(getModuleControlsDisabled) const [expandedCard, setExpandedCard] = React.useState( modules.length > 0 ? modules[0].serial : '' ) @@ -47,7 +48,7 @@ export const ModuleLiveStatusCards = () => { toggleCard={makeToggleCard(module.serial)} isCardExpanded={expandedCard === module.serial} sendModuleCommand={sendModuleCommand} - allowInteraction={!isProtocolRunning} + controlDisabledReason={controlDisabledReason} /> ) case THERMOCYCLER: @@ -58,7 +59,7 @@ export const ModuleLiveStatusCards = () => { toggleCard={makeToggleCard(module.serial)} isCardExpanded={expandedCard === module.serial} sendModuleCommand={sendModuleCommand} - allowInteraction={!isProtocolRunning} + controlDisabledReason={controlDisabledReason} /> ) case MAGDECK: diff --git a/app/src/modules/__tests__/selectors.test.js b/app/src/modules/__tests__/selectors.test.js index d85b576042c..90f9635e91b 100644 --- a/app/src/modules/__tests__/selectors.test.js +++ b/app/src/modules/__tests__/selectors.test.js @@ -19,6 +19,11 @@ const mockGetProtocolModules: JestMockFn< $Call > = RobotSelectors.getModules +const mockGetProtocolIsRunning: JestMockFn< + [State], + $Call +> = RobotSelectors.getIsRunning + type SelectorSpec = {| name: string, selector: (State, ...Array) => mixed, @@ -121,6 +126,99 @@ const SPECS: Array = [ { _id: 2, slot: '3', name: 'magdeck' }, ], }, + { + name: 'getMissingModules returns protocol modules without attached modules', + selector: Selectors.getMissingModules, + state: { + modules: { + robotName: { + modulesById: { + abc123: Fixtures.mockTemperatureModule, + }, + }, + }, + }, + args: [], + before: () => { + mockGetConnectedRobotName.mockReturnValue('robotName') + mockGetProtocolModules.mockReturnValue([ + { _id: 0, slot: '1', name: 'thermocycler' }, + { _id: 1, slot: '2', name: 'tempdeck' }, + { _id: 2, slot: '3', name: 'magdeck' }, + ]) + }, + expected: [ + { _id: 0, slot: '1', name: 'thermocycler' }, + { _id: 2, slot: '3', name: 'magdeck' }, + ], + }, + { + name: 'getModuleControlsDisabled returns connect message if not connected', + selector: Selectors.getModuleControlsDisabled, + state: { modules: {} }, + args: ['someOtherRobotName'], + before: () => { + mockGetConnectedRobotName.mockReturnValue('robotName') + }, + expected: expect.stringMatching(/connect to robot/i), + }, + { + name: 'getModuleControlsDisabled returns running message if running', + selector: Selectors.getModuleControlsDisabled, + state: { modules: {} }, + args: ['robotName'], + before: () => { + mockGetConnectedRobotName.mockReturnValue('robotName') + mockGetProtocolIsRunning.mockReturnValue(true) + }, + expected: expect.stringMatching(/protocol is running/i), + }, + { + name: 'getModuleControlsDisabled returns null if can control', + selector: Selectors.getModuleControlsDisabled, + state: { modules: {} }, + args: ['robotName'], + before: () => { + mockGetConnectedRobotName.mockReturnValue('robotName') + mockGetProtocolIsRunning.mockReturnValue(false) + }, + expected: null, + }, + { + name: + 'getModuleControlsDisabled returns connect message if not connected and no robotName passed', + selector: Selectors.getModuleControlsDisabled, + state: { modules: {} }, + args: [], + before: () => { + mockGetConnectedRobotName.mockReturnValue(null) + }, + expected: expect.stringMatching(/connect to robot/i), + }, + { + name: + 'getModuleControlsDisabled returns running message if running and no robotName passed', + selector: Selectors.getModuleControlsDisabled, + state: { modules: {} }, + args: [], + before: () => { + mockGetConnectedRobotName.mockReturnValue('robotName') + mockGetProtocolIsRunning.mockReturnValue(true) + }, + expected: expect.stringMatching(/protocol is running/i), + }, + { + name: + 'getModuleControlsDisabled returns null if no robotName passed and can control', + selector: Selectors.getModuleControlsDisabled, + state: { modules: {} }, + args: [], + before: () => { + mockGetConnectedRobotName.mockReturnValue('robotName') + mockGetProtocolIsRunning.mockReturnValue(false) + }, + expected: null, + }, ] describe('robot api selectors', () => { diff --git a/app/src/modules/i18n.js b/app/src/modules/i18n.js new file mode 100644 index 00000000000..4775a711687 --- /dev/null +++ b/app/src/modules/i18n.js @@ -0,0 +1,7 @@ +// @flow +// TODO(mc, 2020-03-16): i18n + +export const CONNECT_FOR_MODULE_CONTROL = 'Connect to robot to control modules' + +export const CANNOT_CONTROL_MODULE_WHILE_RUNNING = + 'Cannot control modules while protocol is running' diff --git a/app/src/modules/selectors.js b/app/src/modules/selectors.js index f0d0f62ecea..65a1d788f11 100644 --- a/app/src/modules/selectors.js +++ b/app/src/modules/selectors.js @@ -4,6 +4,7 @@ import sortBy from 'lodash/sortBy' import countBy from 'lodash/countBy' import { selectors as RobotSelectors } from '../robot' +import * as Copy from './i18n' import * as Types from './types' import type { State } from '../types' @@ -70,3 +71,24 @@ export const getMissingModules: ( ) } ) + +// selector to return a reason for module control being disabled if they +// should be disabled. Omit `robotName` arg to refer to currently connected +// RPC robot +export const getModuleControlsDisabled = (state: State, robotName?: string) => { + const connectedRobotName = RobotSelectors.getConnectedRobotName(state) + const protocolIsRunning = RobotSelectors.getIsRunning(state) + + if ( + connectedRobotName === null || + (robotName != null && connectedRobotName !== robotName) + ) { + return Copy.CONNECT_FOR_MODULE_CONTROL + } + + if (protocolIsRunning) { + return Copy.CANNOT_CONTROL_MODULE_WHILE_RUNNING + } + + return null +} From 1d749fc67b054687b1b78ba1de872d93473df018 Mon Sep 17 00:00:00 2001 From: Seth Foster Date: Wed, 18 Mar 2020 12:14:24 -0400 Subject: [PATCH 4/4] fixup: remove old text-align: justify from parent of tooltip --- app/src/components/ModuleLiveStatusCards/styles.css | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/components/ModuleLiveStatusCards/styles.css b/app/src/components/ModuleLiveStatusCards/styles.css index c48596310fb..095c9bdd975 100644 --- a/app/src/components/ModuleLiveStatusCards/styles.css +++ b/app/src/components/ModuleLiveStatusCards/styles.css @@ -21,7 +21,6 @@ updated component library cards after redesign/refactor .card_row { padding: 1rem 0.5rem; - text-align: justify; font-size: var(--fs-body-1); display: flex; align-items: center;