Skip to content

Commit

Permalink
web: update the split button UI to more gracefully handle border mism…
Browse files Browse the repository at this point in the history
…atch (#5531)
  • Loading branch information
nicks authored Feb 24, 2022
1 parent 6fd46e9 commit 1078b1b
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 44 deletions.
9 changes: 6 additions & 3 deletions web/src/ApiButton.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ export default {
(Story: any) => (
<MemoryRouter initialEntries={["/"]}>
<TiltSnackbarProvider>
<div style={{ margin: "-1rem" }}>
<Story />
</div>
<Story />
</TiltSnackbarProvider>
</MemoryRouter>
),
Expand All @@ -37,6 +35,11 @@ export const SimpleButton = () => {
return <StyledButton uiButton={button} />
}

export const RequiresConfirmation = () => {
const button = makeUIButton({ requiresConfirmation: true })
return <StyledButton uiButton={button} />
}

export const ThreeTextInputs = () => {
const inputs: UIInputSpec[] = [1, 2, 3].map((i) => textField(`text${i}`))
const button = makeUIButton({ inputSpecs: inputs })
Expand Down
64 changes: 28 additions & 36 deletions web/src/BulkApiButton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ButtonClassKey, ButtonGroup, ButtonProps } from "@material-ui/core"
import { ClassNameMap } from "@material-ui/styles"
import React, { useLayoutEffect, useState } from "react"
import React, { useLayoutEffect, useMemo, useState } from "react"
import styled from "styled-components"
import { AnalyticsType, Tags } from "./analytics"
import {
Expand Down Expand Up @@ -34,7 +34,6 @@ import { UIButton } from "./types"
type BulkApiButtonProps = ButtonProps & {
bulkAction: BulkAction
buttonText: string
className?: string
onClickCallback?: () => void
requiresConfirmation: boolean
targetToggleState?: ApiButtonToggleState
Expand Down Expand Up @@ -86,28 +85,17 @@ const BulkButtonGroup = styled(ButtonGroup)<{ disabled?: boolean }>`
cursor: not-allowed;
`}
/* Adjust the border radius of buttons to achieve the "rounded bar" look */
&.firstButtonGroupInRow {
${BulkButtonElementRoot} {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-right: 0;
}
}
&.middleButtonGroupInRow {
${BulkButtonElementRoot} {
border-radius: 0;
border-right: 0;
}
}
&.lastButtonGroupInRow {
& + &:not(.isConfirming) {
margin-left: -4px;
${BulkButtonElementRoot} {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
}
& + &.isConfirming {
margin-left: 4px;
}
`

// Helpers
Expand Down Expand Up @@ -250,7 +238,6 @@ export function BulkApiButton(props: BulkApiButtonProps) {
const {
bulkAction,
buttonText,
className,
targetToggleState,
requiresConfirmation,
onClickCallback,
Expand All @@ -263,29 +250,34 @@ export function BulkApiButton(props: BulkApiButtonProps) {
const [loading, setLoading] = useState(false)
const [confirming, setConfirming] = useState(false)

const analyticsTags: Tags = {
component: ApiButtonType.Global,
type: AnalyticsType.Grid,
bulkCount: String(uiButtons.length),
bulkAction,
}
let buttonCount = String(uiButtons.length)
const analyticsTags: Tags = useMemo(() => {
let tags: Tags = {
component: ApiButtonType.Global,
type: AnalyticsType.Grid,
bulkCount: buttonCount,
bulkAction,
}

if (targetToggleState) {
// The `toggleValue` reflects the value of the buttons
// when they are clicked, not their updated values
analyticsTags.toggleValue =
targetToggleState === ApiButtonToggleState.On
? ApiButtonToggleState.Off
: ApiButtonToggleState.On
}
if (targetToggleState) {
// The `toggleValue` reflects the value of the buttons
// when they are clicked, not their updated values
tags.toggleValue =
targetToggleState === ApiButtonToggleState.On
? ApiButtonToggleState.Off
: ApiButtonToggleState.On
}

return tags
}, [buttonCount, bulkAction, targetToggleState])

const bulkActionDisabled = !canBulkButtonBeToggled(
uiButtons,
targetToggleState
)
const disabled = loading || bulkActionDisabled || false
const buttonGroupClassName = `${className || ""} ${
disabled ? "isDisabled" : "isEnabled"
const buttonGroupClassName = `${disabled ? "isDisabled" : "isEnabled"} ${
confirming ? "isConfirming" : ""
}`

// If the bulk action isn't available while the bulk button
Expand Down
7 changes: 5 additions & 2 deletions web/src/HudErrorContext.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useContext } from "react"
import React, { useContext, useMemo } from "react"

// allows consumers to set an error message to show up in the HUD
type HudErrorContext = {
Expand All @@ -16,8 +16,11 @@ export function useHudErrorContext() {
export function HudErrorContextProvider(
props: React.PropsWithChildren<HudErrorContext>
) {
let value = useMemo(() => {
return { setError: props.setError }
}, [props.setError])
return (
<hudErrorContext.Provider value={{ setError: props.setError }}>
<hudErrorContext.Provider value={value}>
{props.children}
</hudErrorContext.Provider>
)
Expand Down
50 changes: 50 additions & 0 deletions web/src/OverviewTableBulkActions.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react"
import { MemoryRouter } from "react-router"
import Features, { FeaturesTestProvider, Flag } from "./feature"
import { OverviewTableBulkActions } from "./OverviewTableBulkActions"
import { ResourceSelectionProvider } from "./ResourceSelectionContext"
import { disableButton } from "./testdata"

type UIButton = Proto.v1alpha1UIButton
type UIInputSpec = Proto.v1alpha1UIInputSpec
type UITextInputSpec = Proto.v1alpha1UITextInputSpec
type UIInputStatus = Proto.v1alpha1UIInputStatus

export default {
title: "New UI/Overview/OverviewTableBulkActions",
decorators: [
(Story: any) => {
const features = new Features({
[Flag.Labels]: true,
[Flag.DisableResources]: true,
})
return (
<MemoryRouter initialEntries={["/"]}>
<FeaturesTestProvider value={features}>
<ResourceSelectionProvider initialValuesForTesting={["fe", "api"]}>
<Story />
</ResourceSelectionProvider>
</FeaturesTestProvider>
</MemoryRouter>
)
},
],
}

export const BulkActionsAllEnabled = () => {
const a = disableButton("fe", true)
const b = disableButton("api", true)
return <OverviewTableBulkActions uiButtons={[a, b]} />
}

export const BulkActionsAllDisabled = () => {
const a = disableButton("fe", false)
const b = disableButton("api", false)
return <OverviewTableBulkActions uiButtons={[a, b]} />
}

export const BulkActionsPartial = () => {
const a = disableButton("fe", false)
const b = disableButton("api", true)
return <OverviewTableBulkActions uiButtons={[a, b]} />
}
4 changes: 1 addition & 3 deletions web/src/OverviewTableBulkActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,13 @@ export function OverviewTableBulkActions({
return null
}

const onClickCallback = () => clearSelections()
const onClickCallback = clearSelections

return (
<BulkActionMenu aria-label="Bulk resource actions">
<BulkApiButton
bulkAction={BulkAction.Disable}
buttonText="Enable"
className="firstButtonGroupInRow"
requiresConfirmation={false}
uiButtons={actionButtons[BulkAction.Disable]}
targetToggleState={ApiButtonToggleState.On}
Expand All @@ -107,7 +106,6 @@ export function OverviewTableBulkActions({
<BulkApiButton
bulkAction={BulkAction.Disable}
buttonText="Disable"
className="lastButtonGroupInRow"
requiresConfirmation={true}
uiButtons={actionButtons[BulkAction.Disable]}
targetToggleState={ApiButtonToggleState.Off}
Expand Down

0 comments on commit 1078b1b

Please sign in to comment.