0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-10 23:36:14 -05:00

Admin X Tiers Fields (#17332)

refs. https://github.com/TryGhost/Product/issues/3580

- The currency dropdown needed a small version of the regular select
- The input field needed a version with a right-side placeholder text
This commit is contained in:
Peter Zimon 2023-07-12 18:23:51 +02:00 committed by GitHub
parent 42d87d1437
commit 497d1be2ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 18 deletions

View file

@ -4,7 +4,7 @@ import type {Meta, StoryObj} from '@storybook/react';
import Select, {SelectOption} from './Select';
const meta = {
title: 'Global / Simple select',
title: 'Global / Form / Select',
component: Select,
tags: ['autodocs'],
decorators: [(_story: any) => (<div style={{maxWidth: '400px'}}>{_story()}</div>)],
@ -61,6 +61,13 @@ export const WithHint: Story = {
}
};
export const ExtraSmall: Story = {
args: {
options: selectOptions,
size: 'xs'
}
};
export const WithSelectedOption: Story = {
render: function Component(args) {
const [, updateArgs] = useArgs();

View file

@ -11,6 +11,7 @@ export interface SelectOption {
export interface SelectProps {
title?: string;
size?: 'xs' | 'md';
prompt?: string;
options: SelectOption[];
selectedOption?: string
@ -18,6 +19,7 @@ export interface SelectProps {
error?:boolean;
hint?: React.ReactNode;
clearBg?: boolean;
border?: boolean;
containerClassName?: string;
selectClassName?: string;
optionClassName?: string;
@ -26,6 +28,7 @@ export interface SelectProps {
const Select: React.FC<SelectProps> = ({
title,
size = 'md',
prompt,
options,
selectedOption,
@ -33,6 +36,7 @@ const Select: React.FC<SelectProps> = ({
error,
hint,
clearBg = true,
border = true,
containerClassName,
selectClassName,
optionClassName,
@ -49,7 +53,7 @@ const Select: React.FC<SelectProps> = ({
containerClasses = clsx(
'relative w-full after:pointer-events-none',
`after:absolute after:block after:h-2 after:w-2 after:rotate-45 after:border-[1px] after:border-l-0 after:border-t-0 after:border-grey-900 after:content-['']`,
title ? 'after:top-[14px]' : 'after:top-[14px]',
size === 'xs' ? 'after:top-[6px]' : 'after:top-[14px]',
clearBg ? 'after:right-0' : 'after:right-4'
);
}
@ -61,7 +65,9 @@ const Select: React.FC<SelectProps> = ({
let selectClasses = '';
if (!unstyled) {
selectClasses = clsx(
'h-10 w-full cursor-pointer appearance-none border-b py-2 pr-5 outline-none',
size === 'xs' ? 'h-6 py-0 pr-3 text-xs' : 'h-10 py-2 pr-5',
'w-full cursor-pointer appearance-none outline-none',
border && 'border-b',
!clearBg && 'bg-grey-75 px-[10px]',
error ? 'border-red' : 'border-grey-500 hover:border-grey-700 focus:border-black',
(title && !clearBg) && 'mt-2'

View file

@ -58,6 +58,14 @@ export const WithHint: Story = {
}
};
export const WithRightPlaceholder: Story = {
args: {
title: 'Monthly price',
placeholder: '0',
rightPlaceholder: 'USD/month'
}
};
export const PasswordType: Story = {
args: {
title: 'Password',

View file

@ -12,6 +12,7 @@ export type TextFieldProps = React.InputHTMLAttributes<HTMLInputElement> & {
value?: string;
error?: boolean;
placeholder?: string;
rightPlaceholder?: string;
hint?: React.ReactNode;
clearBg?: boolean;
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
@ -33,6 +34,7 @@ const TextField: React.FC<TextFieldProps> = ({
value,
error,
placeholder,
rightPlaceholder,
hint,
clearBg = true,
onChange,
@ -53,10 +55,13 @@ const TextField: React.FC<TextFieldProps> = ({
error ? `border-red` : `${disabled ? 'border-grey-300' : 'border-grey-500 hover:border-grey-700 focus:border-black'}`,
(title && !hideTitle && !clearBg) && `mt-2`,
(disabled ? 'text-grey-700' : ''),
rightPlaceholder && 'peer w-0 grow',
className
);
const field = <input
let field = <></>;
const inputField = <input
ref={inputRef}
className={textFieldClasses || className}
disabled={disabled}
@ -69,6 +74,22 @@ const TextField: React.FC<TextFieldProps> = ({
onChange={onChange}
{...props} />;
if (rightPlaceholder) {
const rightPHClasses = !unstyled && clsx(
'h-10 border-b py-2 text-right text-grey-500',
error ? `border-red` : `${disabled ? 'border-grey-300' : 'border-grey-500 peer-hover:border-grey-700 peer-focus:border-black'}`
);
field = (
<div className='flex w-full items-center'>
{inputField}
<span className={rightPHClasses || ''}>{rightPlaceholder}</span>
</div>
);
} else {
field = inputField;
}
if (title || hint) {
let titleGrey = false;
if (titleColor === 'auto') {

View file

@ -5,6 +5,7 @@ import Icon from '../../../../admin-x-ds/global/Icon';
import Modal from '../../../../admin-x-ds/global/modal/Modal';
import NiceModal, {useModal} from '@ebay/nice-modal-react';
import React from 'react';
import Select from '../../../../admin-x-ds/global/form/Select';
import SortableList from '../../../../admin-x-ds/global/SortableList';
import TextField from '../../../../admin-x-ds/global/form/TextField';
import TierDetailPreview from './TierDetailPreview';
@ -88,22 +89,40 @@ const TierDetailModal: React.FC<TierDetailModalProps> = ({tier}) => {
onChange={e => updateForm(state => ({...state, description: e.target.value}))}
/>
<div className='flex gap-10'>
<div className='flex basis-1/2 flex-col gap-2'>
<TextField
placeholder='1'
title='Prices'
value={formState.monthly_price}
onChange={e => updateForm(state => ({...state, monthly_price: e.target.value.replace(/[^\d.]/, '')}))}
/>
<TextField
placeholder='10'
value={formState.yearly_price}
onChange={e => updateForm(state => ({...state, yearly_price: e.target.value.replace(/[^\d.]/, '')}))}
/>
<div className='basis-1/2'>
<div className='mb-1 flex h-6 items-center justify-between'>
<Heading level={6}>Prices</Heading>
<div className='w-10'>
<Select
border={false}
options={[
{label: 'USD', value: 'US Dollaz'},
{label: 'HUF', value: 'Hungarian Dollaz'}
]}
selectClassName='font-medium'
size='xs'
onSelect={() => {}}
/>
</div>
</div>
<div className='flex flex-col gap-2'>
<TextField
placeholder='1'
rightPlaceholder='USD/month'
value={formState.monthly_price}
onChange={e => updateForm(state => ({...state, monthly_price: e.target.value.replace(/[^\d.]/, '')}))}
/>
<TextField
placeholder='10'
rightPlaceholder='USD/year'
value={formState.yearly_price}
onChange={e => updateForm(state => ({...state, yearly_price: e.target.value.replace(/[^\d.]/, '')}))}
/>
</div>
</div>
<div className='basis-1/2'>
<div className='flex justify-between'>
<Heading level={6} grey>Add a free trial</Heading>
<div className='mb-1 flex h-6 items-center justify-between'>
<Heading level={6}>Add a free trial</Heading>
<Toggle onChange={() => {}} />
</div>
<TextField
@ -111,6 +130,7 @@ const TierDetailModal: React.FC<TierDetailModalProps> = ({tier}) => {
Members will be subscribed at full price once the trial ends. <a href="https://ghost.org/" rel="noreferrer" target="_blank">Learn more</a>
</>}
placeholder='0'
rightPlaceholder='days'
value={formState.trial_days}
disabled
onChange={e => updateForm(state => ({...state, trial_days: e.target.value.replace(/^[\d.]/, '')}))}