From 5d28f8f14ed411b26d19aaac9d328c705a2f2c8f Mon Sep 17 00:00:00 2001 From: devrk96 Date: Wed, 9 Sep 2020 08:46:44 +0530 Subject: [PATCH 1/3] Feature: Tooltip component with story (#498) * tooltip component implemented with story * default props fixed * used constant classes names * blueprint classname corrected --- app/client/src/components/ads/Tooltip.tsx | 59 +++++++++++++++++++ app/client/src/components/ads/common.tsx | 2 + .../components/stories/Tooltip.stories.tsx | 34 +++++++++++ 3 files changed, 95 insertions(+) create mode 100644 app/client/src/components/ads/Tooltip.tsx create mode 100644 app/client/src/components/stories/Tooltip.stories.tsx diff --git a/app/client/src/components/ads/Tooltip.tsx b/app/client/src/components/ads/Tooltip.tsx new file mode 100644 index 0000000000..1a021f7b8d --- /dev/null +++ b/app/client/src/components/ads/Tooltip.tsx @@ -0,0 +1,59 @@ +import React from "react"; +import { CommonComponentProps } from "./common"; +import styled from "styled-components"; +import { Position, Tooltip, Classes } from "@blueprintjs/core"; +import { Classes as CsClasses } from "./common"; + +type Variant = "dark" | "light"; + +type TooltipProps = CommonComponentProps & { + content: JSX.Element | string; + position?: Position; + children: JSX.Element; + variant?: Variant; +}; + +const TooltipWrapper = styled.div<{ variant?: Variant }>` + .${Classes.TOOLTIP} .${Classes.POPOVER_CONTENT} { + padding: 10px 12px; + border-radius: 0px; + background-color: ${props => + props.variant === "dark" + ? props.theme.colors.blackShades[0] + : props.theme.colors.blackShades[8]}; + } + div.${Classes.POPOVER_ARROW} { + display: block; + } + .${Classes.TOOLTIP} { + box-shadow: 0px 12px 20px rgba(0, 0, 0, 0.35);a + } + .${Classes.TOOLTIP} .${CsClasses.BP3_POPOVER_ARROW_BORDER}, + &&&& .${Classes.TOOLTIP} .${CsClasses.BP3_POPOVER_ARROW_FILL} { + fill: ${props => + props.variant === "dark" + ? props.theme.colors.blackShades[0] + : props.theme.colors.blackShades[8]}; + } +`; + +const TooltipComponent = (props: TooltipProps) => { + return ( + + + {props.children} + + + ); +}; + +TooltipComponent.defaultProps = { + position: Position.TOP, + variant: "dark", +}; + +export default TooltipComponent; diff --git a/app/client/src/components/ads/common.tsx b/app/client/src/components/ads/common.tsx index a95628d13e..a0e3f44750 100644 --- a/app/client/src/components/ads/common.tsx +++ b/app/client/src/components/ads/common.tsx @@ -15,6 +15,8 @@ export type ThemeProp = { export enum Classes { ICON = "cs-icon", TEXT = "cs-text", + BP3_POPOVER_ARROW_BORDER = "bp3-popover-arrow-border", + BP3_POPOVER_ARROW_FILL = "bp3-popover-arrow-fill", SPINNER = "cs-spinner", } diff --git a/app/client/src/components/stories/Tooltip.stories.tsx b/app/client/src/components/stories/Tooltip.stories.tsx new file mode 100644 index 0000000000..c5cddb6cc3 --- /dev/null +++ b/app/client/src/components/stories/Tooltip.stories.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import { select, withKnobs } from "@storybook/addon-knobs"; +import { withDesign } from "storybook-addon-designs"; +import { Position } from "@blueprintjs/core"; +import TooltipComponent from "components/ads/Tooltip"; +import { StoryWrapper } from "components/ads/common"; +import Text, { TextType } from "components/ads/Text"; +import Button from "components/ads/Button"; + +export default { + title: "Tooltip", + component: TooltipComponent, + decorators: [withKnobs, withDesign], +}; + +export const MenuStory = () => ( + +
+ + This is a tooltip + + } + variant={select("variant", ["dark", "light"], "dark")} + > + + Hover to show tooltip + + +
+
+); From 6fbab2078d92b38557002843d535d08138114138 Mon Sep 17 00:00:00 2001 From: devrk96 Date: Wed, 9 Sep 2020 08:47:49 +0530 Subject: [PATCH 2/3] Feature: Checkbox component (#475) * checkbox disabled state added and checked UI fixed * checkbox imported corrected in story * checkbox implemented using input and label elements. * align and isChecked props removed * used styled component for span --- app/client/src/components/ads/Checkbox.tsx | 102 ++++++++++++++++-- .../components/stories/Checkbox.stories.tsx | 22 ++++ 2 files changed, 116 insertions(+), 8 deletions(-) create mode 100644 app/client/src/components/stories/Checkbox.stories.tsx diff --git a/app/client/src/components/ads/Checkbox.tsx b/app/client/src/components/ads/Checkbox.tsx index 7f7325bdee..8221668fa6 100644 --- a/app/client/src/components/ads/Checkbox.tsx +++ b/app/client/src/components/ads/Checkbox.tsx @@ -1,14 +1,100 @@ import { CommonComponentProps } from "./common"; +import React, { useState } from "react"; +import styled from "styled-components"; type CheckboxProps = CommonComponentProps & { label: string; - isChecked: boolean; - onCheckChange: (isChecked: boolean) => void; - isLoading: boolean; - align: "left" | "right"; - cypressSelector?: string; + onCheckChange?: (isChecked: boolean) => void; }; -export default function Checkbox(props: CheckboxProps) { - return null; -} +const Checkmark = styled.span<{ + disabled?: boolean; + isChecked?: boolean; +}>` + position: absolute; + top: 1px; + left: 0; + width: ${props => props.theme.spaces[8]}px; + height: ${props => props.theme.spaces[8]}px; + background-color: ${props => + props.isChecked + ? props.disabled + ? props.theme.colors.blackShades[3] + : props.theme.colors.info.main + : "transparent"}; + border: 2px solid + ${props => + props.isChecked + ? props.disabled + ? props.theme.colors.blackShades[3] + : props.theme.colors.info.main + : props.theme.colors.blackShades[4]}; + + &::after { + content: ""; + position: absolute; + display: none; + top: 0px; + left: 4px; + width: 6px; + height: 11px; + border: solid + ${props => + props.disabled ? "#565656" : props.theme.colors.blackShades[9]}; + border-width: 0 2px 2px 0; + transform: rotate(45deg); + } +`; + +const StyledCheckbox = styled.label<{ + disabled?: boolean; +}>` + position: relative; + display: block; + width: 100%; + cursor: ${props => (props.disabled ? "not-allowed" : "pointer")}; + font-weight: ${props => props.theme.typography.p1.fontWeight}; + font-size: ${props => props.theme.typography.p1.fontSize}px; + line-height: ${props => props.theme.typography.p1.lineHeight}px; + letter-spacing: ${props => props.theme.typography.p1.letterSpacing}px; + color: ${props => props.theme.colors.blackShades[7]}; + padding-left: ${props => props.theme.spaces[12] - 2}px; + + input { + position: absolute; + opacity: 0; + cursor: pointer; + height: 0; + width: 0; + } + + input:checked ~ ${Checkmark}:after { + display: block; + } +`; + +const Checkbox = (props: CheckboxProps) => { + const [checked, setChecked] = useState(false); + + const onChangeHandler = (checked: boolean) => { + setChecked(checked); + props.onCheckChange && props.onCheckChange(checked); + }; + + return ( + + {props.label} + ) => + onChangeHandler(e.target.checked) + } + /> + + + ); +}; + +export default Checkbox; diff --git a/app/client/src/components/stories/Checkbox.stories.tsx b/app/client/src/components/stories/Checkbox.stories.tsx new file mode 100644 index 0000000000..12572f8613 --- /dev/null +++ b/app/client/src/components/stories/Checkbox.stories.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import { withKnobs, boolean, text } from "@storybook/addon-knobs"; +import { withDesign } from "storybook-addon-designs"; +import { StoryWrapper } from "./Tabs.stories"; +import { action } from "@storybook/addon-actions"; +import Checkbox from "components/ads/Checkbox"; + +export default { + title: "Checkbox", + component: Checkbox, + decorators: [withKnobs, withDesign], +}; + +export const CustomCheckbox = () => ( + + + +); From 34ff72b50541cd3e3e85ba32a45bbf31eab254f1 Mon Sep 17 00:00:00 2001 From: devrk96 Date: Wed, 9 Sep 2020 08:48:54 +0530 Subject: [PATCH 3/3] Feature: Radio group component (#481) * Radio group component with story implemented * radio group implemented using native input and label. * radio component height fixed in story * disabled name in theme corrected for radio * align prop removed --- app/client/src/components/ads/Radio.tsx | 156 +++++++++++++++++- .../src/components/stories/Radio.stories.tsx | 56 +++++++ app/client/src/constants/DefaultTheme.tsx | 3 + 3 files changed, 208 insertions(+), 7 deletions(-) create mode 100644 app/client/src/components/stories/Radio.stories.tsx diff --git a/app/client/src/components/ads/Radio.tsx b/app/client/src/components/ads/Radio.tsx index e66b8693f1..7486b67c82 100644 --- a/app/client/src/components/ads/Radio.tsx +++ b/app/client/src/components/ads/Radio.tsx @@ -1,12 +1,154 @@ import { CommonComponentProps } from "./common"; +import React, { useState, useEffect } from "react"; +import styled from "styled-components"; -type RadioProps = CommonComponentProps & { - align?: "horizontal" | "vertical" | "column" | "row"; - columns?: number; - rows?: number; - value?: string; +type OptionProps = { + label: string; + value: string; + disabled?: boolean; + onSelect?: (value: string) => void; }; -export default function Radio(props: RadioProps) { - return null; +type RadioProps = CommonComponentProps & { + columns?: number; + rows?: number; + defaultValue: string; + onSelect?: (value: string) => void; + options: OptionProps[]; +}; + +const RadioGroup = styled.div<{ + rows?: number; +}>` + display: flex; + flex-wrap: wrap; + ${props => + props.rows && props.rows > 0 + ? ` + flex-direction: column; + height: 100%; + ` + : null}; +`; + +const Radio = styled.label<{ + disabled?: boolean; + columns?: number; + rows?: number; +}>` + display: block; + position: relative; + padding-left: ${props => props.theme.spaces[12] - 2}px; + cursor: ${props => (props.disabled ? "not-allowed" : "pointer")}; + font-size: ${props => props.theme.typography.p1.fontSize}px; + font-weight: ${props => props.theme.typography.p1.fontWeight}; + line-height: ${props => props.theme.typography.p1.lineHeight}px; + letter-spacing: ${props => props.theme.typography.p1.letterSpacing}px; + color: ${props => props.theme.colors.blackShades[9]}; + ${props => + props.rows && props.rows > 0 + ? `flex-basis: calc(100% / ${props.rows})` + : null}; + ${props => + props.columns && props.columns > 0 + ? ` + flex-basis: calc(100% / ${props.columns}); + margin-bottom: ${props.theme.spaces[11] + 1}px; + ` + : null}; + + input { + position: absolute; + opacity: 0; + cursor: pointer; + } + + .checkbox { + position: absolute; + top: 0; + left: 0; + width: ${props => props.theme.spaces[8]}px; + height: ${props => props.theme.spaces[8]}px; + background-color: transparent; + border: ${props => props.theme.spaces[1] - 2}px solid + ${props => props.theme.colors.blackShades[4]}; + border-radius: 50%; + margin-top: ${props => props.theme.spaces[0]}px; + } + + .checkbox:after { + content: ""; + position: absolute; + display: none; + } + + input:checked ~ .checkbox:after { + display: block; + } + + input:disabled ~ .checkbox:after { + background-color: ${props => props.theme.colors.radio.disabled}; + } + + .checkbox:after { + content: ""; + position: absolute; + width: ${props => props.theme.spaces[4]}px; + height: ${props => props.theme.spaces[4]}px; + ${props => + props.disabled + ? `background-color: ${props.theme.colors.radio.disabled}` + : `background-color: ${props.theme.colors.info.main};`}; + top: ${props => props.theme.spaces[1] - 2}px; + left: ${props => props.theme.spaces[1] - 2}px; + border-radius: 50%; + } +`; + +export default function RadioComponent(props: RadioProps) { + const [selected, setSelected] = useState(props.defaultValue); + + useEffect(() => { + if (props.rows && props.columns && props.rows > 0 && props.columns > 0) { + console.error( + "Please pass either rows prop or column prop but not both.", + ); + } + }, [props]); + + useEffect(() => { + setSelected(props.defaultValue); + }, [props.defaultValue]); + + const onChangeHandler = (value: string) => { + setSelected(value); + props.onSelect && props.onSelect(value); + }; + + return ( + onChangeHandler(e.target.value)} + > + {props.options.map((option: OptionProps, index: number) => ( + + {option.label} + option.onSelect && option.onSelect(e.target.value)} + checked={selected === option.value} + name="radio" + /> + + + ))} + + ); } diff --git a/app/client/src/components/stories/Radio.stories.tsx b/app/client/src/components/stories/Radio.stories.tsx new file mode 100644 index 0000000000..3f69522581 --- /dev/null +++ b/app/client/src/components/stories/Radio.stories.tsx @@ -0,0 +1,56 @@ +import React from "react"; +import { + withKnobs, + select, + boolean, + text, + number, +} from "@storybook/addon-knobs"; +import { withDesign } from "storybook-addon-designs"; +import { StoryWrapper } from "./Tabs.stories"; +import RadioComponent from "components/ads/Radio"; +import { action } from "@storybook/addon-actions"; + +export default { + title: "Radio", + component: RadioComponent, + decorators: [withKnobs, withDesign], +}; + +export const Radio = () => ( + +
+ +
+
+); diff --git a/app/client/src/constants/DefaultTheme.tsx b/app/client/src/constants/DefaultTheme.tsx index 2d110ce332..32471b2d9f 100644 --- a/app/client/src/constants/DefaultTheme.tsx +++ b/app/client/src/constants/DefaultTheme.tsx @@ -572,6 +572,9 @@ export const theme: Theme = { darker: "#2B1A1D", darkest: "#462F32", }, + radio: { + disabled: "#565656", + }, primaryOld: Colors.GREEN, primaryDarker: Colors.JUNGLE_GREEN, primaryDarkest: Colors.JUNGLE_GREEN_DARKER,