Skip to content

Commit

Permalink
[core] feat: SwitchCard component (#6250)
Browse files Browse the repository at this point in the history
Co-authored-by: Adi Dahiya <adahiya@palantir.com>
  • Loading branch information
CPerinet and adidahiya authored Oct 5, 2023
1 parent 951e45d commit 4603072
Show file tree
Hide file tree
Showing 21 changed files with 538 additions and 104 deletions.
15 changes: 15 additions & 0 deletions packages/core/src/common/_variables-extended.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,18 @@ $icon-classes: (
".#{$ns}-icon-standard",
".#{$ns}-icon-large"
) !default;

$elevation-shadows: (
$pt-elevation-shadow-0
$pt-elevation-shadow-1
$pt-elevation-shadow-2
$pt-elevation-shadow-3
$pt-elevation-shadow-4
);
$dark-elevation-shadows: (
$pt-dark-elevation-shadow-0
$pt-dark-elevation-shadow-1
$pt-dark-elevation-shadow-2
$pt-dark-elevation-shadow-3
$pt-dark-elevation-shadow-4
);
3 changes: 3 additions & 0 deletions packages/core/src/common/classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ export const CALLOUT_HAS_BODY_CONTENT = `${CALLOUT}-has-body-content`;
export const CALLOUT_ICON = `${CALLOUT}-icon`;

export const CARD = `${NS}-card`;
export const CONTROL_CARD = `${NS}-control-card`;
export const CONTROL_CARD_LABEL = `${CONTROL_CARD}-label`;
export const SWITCH_CONTROL_CARD = `${NS}-switch-control-card`;

export const CARD_LIST = `${NS}-card-list`;
export const CARD_LIST_BORDERED = `${CARD_LIST}-bordered`;
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 @@ -10,6 +10,7 @@
@import "card-list/card-list";
@import "collapse/collapse";
@import "context-menu/context-menu";
@import "control-card/control-card";
@import "divider/divider";
@import "dialog/dialog";
@import "dialog/dialog-body";
Expand Down
21 changes: 7 additions & 14 deletions packages/core/src/components/card-list/card-list.scss
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
@import "../../common/variables";
// Copyright 2023 Palantir Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0.

$card-list-border-width: 1px !default;

// N.B. min-height is calculated as height of a button + vertical padding. We need to add an extra pixel to account for
// the bottom border.
$card-list-item-default-min-height: ($pt-grid-size * 5) + $card-list-border-width !default;
$card-list-item-default-padding: $pt-grid-size (2 * $pt-grid-size) !default;

$card-list-item-small-min-height: ($pt-grid-size * 4) + $card-list-border-width !default;
$card-list-item-small-padding: 7px 15px !default;
@import "../card/card-variables";

.#{$ns}-card-list {
overflow: auto;
Expand All @@ -20,8 +13,8 @@ $card-list-item-small-padding: 7px 15px !default;
border-radius: 0;
box-shadow: none;
display: flex;
min-height: $card-list-item-default-min-height;
padding: $card-list-item-default-padding;
min-height: $card-list-item-min-height;
padding: $card-list-item-padding;

&.#{$ns}-interactive:hover,
&.#{$ns}-interactive:active {
Expand All @@ -43,8 +36,8 @@ $card-list-item-small-padding: 7px 15px !default;
}

&.#{$ns}-compact > .#{$ns}-card {
min-height: $card-list-item-small-min-height;
padding: $card-list-item-small-padding;
min-height: $card-list-item-min-height-compact;
padding: $card-list-item-padding-compact;
}

.#{$ns}-dark & {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/components/card-list/cardList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export const CardList: React.FC<CardListProps> = React.forwardRef((props, ref) =
);
});
CardList.defaultProps = {
bordered: true,
compact: false,
};
CardList.displayName = `${DISPLAYNAME_PREFIX}.CardList`;
30 changes: 30 additions & 0 deletions packages/core/src/components/card/_card-variables.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2023 Palantir Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0.

@import "../../common/variables-extended";

$card-padding: $pt-grid-size * 2 !default;
$card-padding-compact: $pt-grid-size * 1.5 !default;

$card-background-color: $white !default;
$dark-card-background-color: $dark-gray3 !default;

$card-list-border-width: 1px !default;

// stylelint-disable max-line-length

// CardList Card item min-height is calculated as height of a button + vertical padding.
// We need to add an extra pixel to account for the bottom border.
$card-list-item-padding-vertical: $pt-grid-size !default;
$card-list-item-min-height: $pt-button-height + ($card-list-item-padding-vertical * 2) + $card-list-border-width !default;
$card-list-item-padding: $card-list-item-padding-vertical $card-padding !default;

$card-list-item-padding-vertical-compact: 7px !default;
$card-list-item-min-height-compact: $pt-button-height + ($card-list-item-padding-vertical-compact * 2) + $card-list-border-width !default;
$card-list-item-padding-compact: $card-list-item-padding-vertical-compact $card-padding-compact !default;

// CardList ControlCard item min-height is calculated as height of a control indicator + vertical padding
$card-list-control-item-padding-vertical: $card-padding;
$card-list-control-item-min-height: $control-indicator-size + ($card-list-control-item-padding-vertical * 2) + $card-list-border-width !default;
$card-list-control-item-padding-vertical-compact: $card-padding-compact;
$card-list-control-item-min-height-compact: $control-indicator-size + ($card-list-control-item-padding-vertical-compact * 2) + $card-list-border-width !default;
22 changes: 1 addition & 21 deletions packages/core/src/components/card/_card.scss
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2015 Palantir Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0.

@import "../../common/variables";
@import "./card-variables";

/*
Cards
Expand All @@ -21,26 +21,6 @@ Markup:
Styleguide card
*/

$card-padding: $pt-grid-size * 2 !default;

$card-background-color: $white !default;
$dark-card-background-color: $dark-gray3 !default;

$elevation-shadows: (
$pt-elevation-shadow-0
$pt-elevation-shadow-1
$pt-elevation-shadow-2
$pt-elevation-shadow-3
$pt-elevation-shadow-4
);
$dark-elevation-shadows: (
$pt-dark-elevation-shadow-0
$pt-dark-elevation-shadow-1
$pt-dark-elevation-shadow-2
$pt-dark-elevation-shadow-3
$pt-dark-elevation-shadow-4
);

.#{$ns}-card {
background-color: $card-background-color;
border-radius: $pt-border-radius;
Expand Down
9 changes: 6 additions & 3 deletions packages/core/src/components/card/card.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@# Card

A __Card__ is a bounded unit of UI content with a solid background color.
A **Card** is a bounded unit of UI content with a solid background color.

@reactExample CardExample

Expand All @@ -10,10 +10,12 @@ A __Card__ is a bounded unit of UI content with a solid background color.
import { Button, Card, Elevation } from "@blueprintjs/core";

<Card interactive={true} elevation={Elevation.TWO}>
<h5><a href="#">Card heading</a></h5>
<h5>
<a href="#">Card heading</a>
</h5>
<p>Card content</p>
<Button>Submit</Button>
</Card>
</Card>;
```

@## Elevation
Expand All @@ -33,6 +35,7 @@ Note that the `Classes.ELEVATION_*` classes can be used on any element (not just
<h5 class="@ns-heading">

Deprecated API: use [`<Card>`](#core/components/card)

</h5>

CSS APIs for Blueprint components are considered deprecated, as they are verbose, error-prone, and they
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/components/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
@page callout
@page card
@page card-list
@page control-card
@page collapse
@page divider
@page editable-text
Expand Down
42 changes: 42 additions & 0 deletions packages/core/src/components/control-card/_control-card.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2023 Palantir Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0.

@import "../card/card-variables";

// use an extra selector to increase specificity
.#{$ns}-card.#{$ns}-control-card {
&,
.#{$ns}-card-list > & {
// min-height & padding will be set on the label element so that it can take up the full size of the card and
// its entire visual element will be interactive This is partially to work around https://github.com/palantir/blueprint/issues/6251
min-height: auto;
padding: 0;
}
}

.#{$ns}-switch-control-card {
// need a lot of specificity here to override control styles
> .#{$ns}-switch.#{$ns}-control.#{$ns}-align-right {
align-items: center;
display: flex;
flex-direction: row-reverse;
justify-content: space-between;
margin: 0;
padding: $card-padding;
width: calc(100%);

.#{$ns}-card-list & {
min-height: $card-list-control-item-min-height;
padding: $card-list-item-padding;
}

.#{$ns}-card-list.#{$ns}-compact & {
min-height: $card-list-control-item-min-height-compact;
padding: $card-list-item-padding-compact;
}

.#{$ns}-control-indicator {
margin: 0;
}
}
}
34 changes: 34 additions & 0 deletions packages/core/src/components/control-card/control-card.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
tag: new
---

@# Control card

A control card is an interactive [**Card**](#core/components/card) with an embedded form control.
There are a few supported form controls:

- [**SwitchCard**](#core/components/control-card.switch-card)
- CheckboxCard (_coming soon_)
- RadioCard (_coming soon_)

The children of a control card will be used as the `labelElement` of the form control. Users may click anywhere
inside the card to toggle the control state.

@## SwitchCard

Card with an embedded [**Switch**](#core/components/switch) control.

@reactExample SwitchCardExample

@### Props interface

Most of the properties in [**CardProps**](#core/components/card.props-interface) and
[**SwitchProps**](#core/components/switch.props-interface) are available on the root component.

@interface SwitchCardProps

@## Composing with CardList

Control cards work just like regular cards inside a [**CardList**](#core/components/card-list).

@reactExample ControlCardListExample
94 changes: 94 additions & 0 deletions packages/core/src/components/control-card/controlCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright 2023 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 { Classes } from "../../common";
import { DISPLAYNAME_PREFIX, HTMLInputProps } from "../../common/props";
import { Card, CardProps } from "../card/card";
import { ControlProps, Switch } from "../forms/controls";

/**
* Subset of {@link Card} which can be used to adjust its behavior.
*/
type SupportedCardProps = Omit<CardProps, "interactive" | "onChange">;

/**
* Subset of {@link ControlProps} which can be used to adjust its behavior.
*/
type SupportedControlProps = Pick<ControlProps, "checked" | "defaultChecked" | "disabled" | "inputRef" | "onChange">;

export interface ControlCardProps extends SupportedCardProps, SupportedControlProps {
/**
* Which kind of form control to render inside the card.
*/
controlKind: "switch";

// N.B. this is split out of the root properties in the inerface because it would conflict with CardProps' HTMLDivProps
/**
* HTML input attributes to forward to the control `<input>` element.
*/
inputProps?: HTMLInputProps;
}

/**
* ControlCard component, used to render a {@link Card} with a form control.
*
* @internal
*/
export const ControlCard: React.FC<ControlCardProps> = React.forwardRef((props, ref) => {
const {
checked,
children: labelContent,
className,
controlKind,
defaultChecked,
disabled,
inputProps,
inputRef,
onChange,
...cardProps
} = props;

const classes = classNames(Classes.CONTROL_CARD, className, {
[Classes.SWITCH_CONTROL_CARD]: controlKind === "switch",
});

// use a container element to achieve a good flex layout
const labelElement = <div className={Classes.CONTROL_CARD_LABEL}>{labelContent}</div>;
const controlProps: ControlProps = {
checked,
defaultChecked,
disabled,
inputRef,
labelElement,
onChange,
...inputProps,
};

return (
<Card interactive={!disabled} className={classes} ref={ref} {...cardProps}>
{controlKind === "switch" ? (
<Switch inline={true} alignIndicator="right" {...controlProps} />
) : (
labelElement
)}
</Card>
);
});
ControlCard.defaultProps = {};
ControlCard.displayName = `${DISPLAYNAME_PREFIX}.ControlCard`;
33 changes: 33 additions & 0 deletions packages/core/src/components/control-card/switchCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2023 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 * as React from "react";

import { DISPLAYNAME_PREFIX } from "../../common/props";
import { ControlCard, ControlCardProps } from "./controlCard";

export type SwitchCardProps = Omit<ControlCardProps, "controlKind">;

/**
* Switch Card component.
*
* @see https://blueprintjs.com/docs/#core/components/card#switch-card
*/
export const SwitchCard: React.FC<SwitchCardProps> = React.forwardRef((props, ref) => {
return <ControlCard controlKind="switch" ref={ref} {...props} />;
});
SwitchCard.defaultProps = {};
SwitchCard.displayName = `${DISPLAYNAME_PREFIX}.SwitchCard`;
Loading

1 comment on commit 4603072

@adidahiya
Copy link
Contributor

Choose a reason for hiding this comment

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

[core] feat: SwitchCard component (#6250)

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

Please sign in to comment.