Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a multistep dialog component #4462

Merged
merged 17 commits into from
Jan 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/core/src/common/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ $pt-navbar-height: $pt-grid-size * 5 !default;
$pt-z-index-base: 0 !default;
$pt-z-index-content: $pt-z-index-base + 10 !default;
$pt-z-index-overlay: $pt-z-index-content + 10 !default;
$pt-z-index-dialog-header: $pt-z-index-overlay + 10 !default;

// Shadow opacities
$pt-border-shadow-opacity: 0.1 !default;
Expand Down
11 changes: 11 additions & 0 deletions packages/core/src/common/classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ export const DIALOG_FOOTER = `${DIALOG}-footer`;
export const DIALOG_FOOTER_ACTIONS = `${DIALOG}-footer-actions`;
export const DIALOG_HEADER = `${DIALOG}-header`;

export const DIALOG_STEP = `${NS}-dialog-step`;
export const DIALOG_STEP_CONTAINER = `${DIALOG_STEP}-container`;
export const DIALOG_STEP_TITLE = `${DIALOG_STEP}-title`;
export const DIALOG_STEP_ICON = `${DIALOG_STEP}-icon`;

export const DIVIDER = `${NS}-divider`;

export const DRAWER = `${NS}-drawer`;
Expand Down Expand Up @@ -179,6 +184,12 @@ export const MENU_SUBMENU = `${NS}-submenu`;
export const MENU_DIVIDER = `${MENU}-divider`;
export const MENU_HEADER = `${MENU}-header`;

export const MULTISTEP_DIALOG = `${NS}-multistep-dialog`;
export const MULTISTEP_DIALOG_PANELS = `${MULTISTEP_DIALOG}-panels`;
export const MULTISTEP_DIALOG_LEFT_PANEL = `${MULTISTEP_DIALOG}-left-panel`;
export const MULTISTEP_DIALOG_RIGHT_PANEL = `${MULTISTEP_DIALOG}-right-panel`;
export const MULTISTEP_DIALOG_FOOTER = `${MULTISTEP_DIALOG}-footer`;

export const NAVBAR = `${NS}-navbar`;
export const NAVBAR_GROUP = `${NAVBAR}-group`;
export const NAVBAR_HEADING = `${NAVBAR}-heading`;
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/components/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
@import "context-menu/context-menu";
@import "divider/divider";
@import "dialog/dialog";
@import "dialog/multistep-dialog";
@import "drawer/drawer";
@import "editable-text/editable-text";
@import "forms/index";
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/components/dialog/_dialog.scss
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ $dialog-padding: $pt-grid-size * 2 !default;
min-height: $pt-icon-size-large + $dialog-padding;
padding-left: $dialog-padding;
padding-right: $dialog-padding / 4;
z-index: $pt-z-index-dialog-header;

.#{$ns}-icon-large,
.#{$ns}-icon {
Expand Down
130 changes: 130 additions & 0 deletions packages/core/src/components/dialog/_multistep-dialog.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright 2020 Palantir Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0.

@import "~@blueprintjs/icons/src/icons";
@import "../../common/mixins";
@import "../../common/react-transition";
@import "../../common/variables";

$dialog-border-radius: $pt-border-radius * 2 !default;
$step-radius: $pt-border-radius * 2 !default;

.#{$ns}-multistep-dialog-panels {
display: flex;
}

.#{$ns}-multistep-dialog-left-panel {
display: flex;
flex: 1;
flex-direction: column;

.#{$ns}-dark & {
background: $dark-gray2;
}
}

.#{$ns}-multistep-dialog-right-panel {
background-color: $light-gray5;
border-left: 1px solid $pt-divider-black;
border-radius: 0 0 $dialog-border-radius 0;
flex: 2;

.#{$ns}-dark & {
background-color: $dark-gray3;
border-left: 1px solid $pt-dark-divider-black;
}
}

.#{$ns}-multistep-dialog-footer {
background-color: $white;
border-radius: 0 0 $dialog-border-radius 0;
border-top: 1px solid $pt-divider-black;
padding: 10px;

.#{$ns}-dark & {
background: $dark-gray4;
border-top: 1px solid $pt-dark-divider-black;
}
}

.#{$ns}-dialog-step-container {
background-color: $light-gray5;
border-bottom: 1px solid $pt-divider-black;

.#{$ns}-dark & {
background: $dark-gray3;
border-bottom: 1px solid $pt-dark-divider-black;
}

&.#{$ns}-active {
background-color: $white;
.#{$ns}-dark & {
background: $dark-gray4;
}
}
}

.#{$ns}-dialog-step {
align-items: center;
background-color: $light-gray5;
border-radius: $step-radius;
cursor: not-allowed;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is strictly necessary, it can just be grayed out and have a normal pointer with no hover state. Also, what about previous steps? Can't you click on those?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On further review, I realized the semantics of "active" here are a little different from what I expected. That's fine, I think what you have works. but I think it would help to leave a code comment along the lines of "by default, steps are inactive until they are visited"

display: flex;
margin: 4px;
padding: 6px 14px;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed padding to better align with the title icon.


.#{$ns}-dark & {
background: $dark-gray3;
}

// by default, steps are inactive until they are visited
.#{$ns}-active & {
background-color: $white;
cursor: pointer;

.#{$ns}-dark & {
background: $dark-gray4;
}
}

&:hover {
background-color: $light-gray5;

.#{$ns}-dark & {
background: $dark-gray3;
}
}
}

.#{$ns}-dialog-step-icon {
align-items: center;
background-color: $light-gray1;
border-radius: 50%;
color: $white;
display: flex;
height: 25px;
justify-content: center;
width: 25px;

.#{$ns}-dark & {
background-color: $pt-dark-icon-color-disabled;
}

.#{$ns}-active & {
background-color: $blue4;
}
}

.#{$ns}-dialog-step-title {
color: $gray1;
padding-left: 10px;

.#{$ns}-dark & {
color: $pt-dark-text-color-disabled;
}

// by default, step title is active only when the step is selected
&.#{$ns}-active {
color: $blue4;
}
}
39 changes: 36 additions & 3 deletions packages/core/src/components/dialog/dialog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@# Dialog
@# Dialogs

Dialogs present content overlaid over other parts of the UI.

Expand All @@ -13,9 +13,16 @@ We use the term "dialog" to avoid confusion with the adjective.

</div>

Blueprint provides two types of dialogs:

1. Standard dialog: use the `Dialog` component for a dialog that only requires one view.
1. Multistep dialog: use the `MultistepDialog` component for a dialog with multiple sequential views.

@## Dialog

@reactExample DialogExample

@## Props
@### Props

`Dialog` is a stateless React component controlled by the `isOpen` prop.

Expand All @@ -26,7 +33,7 @@ The children you provide to this component are rendered as contents inside the

@interface IDialogProps

@## CSS
@### CSS

You can create dialogs manually using the HTML markup and `@ns-dialog-*` classes below.
However, you should use the [`Dialog` component](#core/components/dialog.props)
Expand All @@ -35,3 +42,29 @@ whenever possible, as they automatically generate some of this markup.
More examples of dialog content are shown below.

@css dialog

@## Multistep dialog

@reactExample MultistepDialogExample

@### Multistep dialog props

`MultistepDialog` is a wrapper around `Dialog` that displays a dialog with multiple steps, each of which maps to a specific panel.

The children you provide to this component are rendered as contents inside the
`Classes.DIALOG` element. Typically, you will want to render a panel with
`Classes.DIALOG_BODY` that contains the body content for each step.
Comment on lines +54 to +56
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should explain a little more about the Step interface similar to the docs for Tab. Also probably worth clarifying that only children of type Step will be rendered.

Copy link
Contributor Author

@ycvfu ycvfu Jan 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, I added info under line 54.


Children of the `MultistepDialog` are filtered down to only `Step` components and rendered in order.
`Step` children are managed by the component; clicking one will change selection.

@interface IMultistepDialogProps

@### Step

`Step` is a minimal wrapper with no functionality of its own&mdash;it is managed entirely by its
parent `MultistepDialog` wrapper. Step title text can be set via the `title` prop.

The associated step panel will be visible when the `Step` is selected.

@interface IStepProps
62 changes: 62 additions & 0 deletions packages/core/src/components/dialog/dialogStep.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2020 Palantir Technologies, Inc. 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.
*/

import classNames from "classnames";
import * as React from "react";
import { polyfill } from "react-lifecycles-compat";

import { AbstractPureComponent2, Classes } from "../../common";
import { DISPLAYNAME_PREFIX, HTMLDivProps, IProps } from "../../common/props";

export type DialogStepId = string | number;

export interface IDialogStepProps extends IProps, Omit<HTMLDivProps, "id" | "title" | "onClick"> {
/**
* Unique identifier used to identify which step is selected.
*/
id: DialogStepId;

/**
* Panel content, rendered by the parent `MultistepDialog` when this step is active.
*/
panel: JSX.Element;

/**
* Space-delimited string of class names applied to multistep dialog panel container.
*/
panelClassName?: string;

/**
* Content of step title element, rendered in a list left of the active panel.
*/
title?: React.ReactNode;
}

@polyfill
export class DialogStep extends AbstractPureComponent2<IDialogStepProps> {
public static displayName = `${DISPLAYNAME_PREFIX}.DialogStep`;

// this component is never rendered directly; see MultistepDialog#renderDialogStepPanel()
/* istanbul ignore next */
public render() {
const { className } = this.props;
return (
<div className={Classes.DIALOG_STEP_CONTAINER}>
<div className={classNames(Classes.DIALOG_STEP, className)} role="dialogsteplist" />
</div>
);
}
}
Loading