Skip to content

Commit

Permalink
Calypso UI Components: DateRange: Refactor "header" and "footer" area…
Browse files Browse the repository at this point in the history
…s of component (#94567)

* simplify

* move buttons to bottom

* add new dateRangeFooter

* refactor renderDateHelp into header

* refactor header content out of index

* styling header + footer

* add description to readme

* fix daterange styling regression

* fix button import
  • Loading branch information
annacmc authored Sep 17, 2024
1 parent f6158b5 commit 45ecb8b
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 80 deletions.
1 change: 1 addition & 0 deletions client/components/date-range/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ These props utilise the [Render Props](https://reactjs.org/docs/render-props.htm
| ---------------------- | ---------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `renderTrigger(props)` | `Function` | undefined | render prop `Function` which will overide the default `DateRangeTrigger` component. Recieves same `props` object passed to `DateRangeTrigger` |
| `renderHeader(props)` | `Function` | undefined | render prop `Function` which will overide the default `DateRangeHeader` component. Recieves same `props` object passed to `DateRangeHeader` |
| `renderFooter(props)` | `Function` | undefined | render prop `Function` which will overide the default `DateRangeFooter` component. Recieves same `props` object passed to `DateRangeFooter` |
| `renderInputs(props)` | `Function` | undefined | render prop `Function` which will overide the default `DateRangeInputs` component. Recieves same `props` object passed to `DateRangeInputs` |

### General guidelines
Expand Down
49 changes: 49 additions & 0 deletions client/components/date-range/footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Button } from '@automattic/components';
import { useTranslate } from 'i18n-calypso';
import { FunctionComponent } from 'react';

// eslint-disable-next-line @typescript-eslint/no-empty-function
const noop = () => {};

interface Props {
onApplyClick: () => void;
onCancelClick: () => void;
applyButtonText: string | null | undefined;
cancelButtonText: string | null | undefined;
}

const DateRangeFooter: FunctionComponent< Props > = ( {
onCancelClick = noop,
onApplyClick = noop,
cancelButtonText,
applyButtonText,
} ) => {
const translate = useTranslate();

const cancelText = cancelButtonText || translate( 'Cancel' );
const applyText = applyButtonText || translate( 'Apply' );

return (
<div className="date-range__popover-footer">
<Button
className="date-range__cancel-btn"
onClick={ onCancelClick }
compact
aria-label={ cancelText }
>
{ cancelText }
</Button>
<Button
className="date-range__apply-btn"
onClick={ onApplyClick }
primary
compact
aria-label={ applyText }
>
{ applyText }
</Button>
</div>
);
};

export default DateRangeFooter;
87 changes: 56 additions & 31 deletions client/components/date-range/header.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,72 @@
import { Button } from '@automattic/components';
import { Gridicon, Button } from '@automattic/components';
import { useTranslate } from 'i18n-calypso';
import { FunctionComponent } from 'react';

// eslint-disable-next-line @typescript-eslint/no-empty-function
const noop = () => {};

interface Props {
onApplyClick: () => void;
onCancelClick: () => void;
applyButtonText: string | null | undefined;
cancelButtonText: string | null | undefined;
customTitle?: string;
startDate: Date | null;
endDate: Date | null;
resetDates: () => void;
}

const DateRangeHeader: FunctionComponent< Props > = ( {
onCancelClick = noop,
onApplyClick = noop,
cancelButtonText,
applyButtonText,
customTitle,
startDate,
endDate,
resetDates,
} ) => {
const translate = useTranslate();

const cancelText = cancelButtonText || translate( 'Cancel' );
const applyText = applyButtonText || translate( 'Apply' );
// Add this check at the beginning of the component
if ( startDate === undefined || endDate === undefined || resetDates === undefined ) {
return null; // or return a loading state
}

const renderDateHelp = () => {
return (
<div className="date-range__info" role="status" aria-live="polite">
{ ! startDate &&
! endDate &&
translate( '{{icon/}} Please select the {{em}}first{{/em}} day.', {
components: {
icon: <Gridicon aria-hidden="true" icon="info" />,
em: <em />,
},
} ) }
{ startDate &&
! endDate &&
translate( '{{icon/}} Please select the {{em}}last{{/em}} day.', {
components: {
icon: <Gridicon aria-hidden="true" icon="info" />,
em: <em />,
},
} ) }
{ startDate && endDate && (
<Button
className="date-range__info-btn"
borderless
compact
onClick={ resetDates }
aria-label={ translate( 'Reset selected dates' ) }
>
{ translate( '{{icon/}} reset selected dates', {
components: { icon: <Gridicon aria-hidden="true" icon="cross-small" /> },
} ) }
</Button>
) }
</div>
);
};

return (
<div className="date-range__popover-header">
<Button
className="date-range__cancel-btn"
onClick={ onCancelClick }
compact
aria-label={ cancelText }
>
{ cancelText }
</Button>
<Button
className="date-range__apply-btn"
onClick={ onApplyClick }
primary
compact
aria-label={ applyText }
>
{ applyText }
</Button>
<div className="date-range__controls">
{ customTitle ? (
<div className="date-range__custom-title">{ customTitle }</div>
) : (
renderDateHelp()
) }
</div>
</div>
);
};
Expand Down
60 changes: 13 additions & 47 deletions client/components/date-range/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Button, Popover, Gridicon } from '@automattic/components';
import { Popover } from '@automattic/components';
import clsx from 'clsx';
import { localize } from 'i18n-calypso';
import moment from 'moment';
import PropTypes from 'prop-types';
import { createRef, Component } from 'react';
import { withLocalizedMoment } from 'calypso/components/localized-moment';
import DateRangePicker from './date-range-picker';
import DateRangeFooter from './footer';
import DateRangeHeader from './header';
import DateRangeInputs from './inputs';
import Shortcuts from './shortcuts';
Expand Down Expand Up @@ -44,6 +45,7 @@ export class DateRange extends Component {
showTriggerClear: PropTypes.bool,
renderTrigger: PropTypes.func,
renderHeader: PropTypes.func,
renderFooter: PropTypes.func,
renderInputs: PropTypes.func,
displayShortcuts: PropTypes.bool,
rootClass: PropTypes.string,
Expand All @@ -60,6 +62,7 @@ export class DateRange extends Component {
showTriggerClear: true,
renderTrigger: ( props ) => <DateRangeTrigger { ...props } />,
renderHeader: ( props ) => <DateRangeHeader { ...props } />,
renderFooter: ( props ) => <DateRangeFooter { ...props } />,
renderInputs: ( props ) => <DateRangeInputs { ...props } />,
displayShortcuts: false,
rootClass: '',
Expand Down Expand Up @@ -413,50 +416,19 @@ export class DateRange extends Component {
this.handleDateRangeChange( startDate, endDate, 'custom_date_range' );
};

renderDateHelp() {
const { startDate, endDate } = this.state;

return (
<div className="date-range__info" role="status" aria-live="polite">
{ ! startDate &&
! endDate &&
this.props.translate( '{{icon/}} Please select the {{em}}first{{/em}} day.', {
components: {
icon: <Gridicon aria-hidden="true" icon="info" />,
em: <em />,
},
} ) }
{ startDate &&
! endDate &&
this.props.translate( '{{icon/}} Please select the {{em}}last{{/em}} day.', {
components: {
icon: <Gridicon aria-hidden="true" icon="info" />,
em: <em />,
},
} ) }
{ startDate && endDate && (
<Button
className="date-range__info-btn"
borderless
compact
onClick={ this.resetDates }
aria-label={ this.props.translate( 'Reset selected dates' ) }
>
{ this.props.translate( '{{icon/}} reset selected dates', {
components: { icon: <Gridicon aria-hidden="true" icon="cross-small" /> },
} ) }
</Button>
) }
</div>
);
}

/**
* Renders the Popover component
* @returns {import('react').Element} the Popover component
*/
renderPopover() {
const headerProps = {
customTitle: this.props.customTitle,
startDate: this.state.startDate,
endDate: this.state.endDate,
resetDates: this.resetDates,
};

const footerProps = {
onApplyClick: this.commitDates,
onCancelClick: this.closePopoverAndRevert,
};
Expand Down Expand Up @@ -486,16 +458,10 @@ export class DateRange extends Component {
{ this.props.overlay && (
<div className="date-range__popover-inner-overlay">{ this.props.overlay }</div>
) }
<div className="date-range__controls">
{ this.props.customTitle ? (
<div className="date-range__custom-title">{ this.props.customTitle }</div>
) : (
this.renderDateHelp()
) }
</div>
{ this.props.renderHeader( headerProps ) }
{ this.props.renderInputs( inputsProps ) }
{ this.renderDatePicker() }
{ this.props.renderHeader( headerProps ) }
{ this.props.renderFooter( footerProps ) }
</div>
{ /* Render shortcuts to the right of the calendar */ }
{ this.props.displayShortcuts && (
Expand Down
8 changes: 6 additions & 2 deletions client/components/date-range/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ $date-range-mobile-layout-switch: $break-small;
}

.date-range__popover-header {
order: 1;
display: flex;
justify-content: left;
}

.date-range__popover-footer {
order: 4;
display: flex;
justify-content: flex-end;
Expand Down Expand Up @@ -277,7 +283,6 @@ $date-range-mobile-layout-switch: $break-small;
}
}
}

.DayPicker-Day--range:not(.DayPicker-Day--disabled):not(.DayPicker-Day--outside) {
background-color: var(--date-range-picker-highlight-color);
.date-picker__day {
Expand Down Expand Up @@ -307,7 +312,6 @@ $date-range-mobile-layout-switch: $break-small;
border-radius: 200px !important;
}
}

.date-range__popover-content { // Styling to fit optional shortcuts sidebar
display: flex;
align-items: stretch; // Ensure children stretch to full height
Expand Down

0 comments on commit 45ecb8b

Please sign in to comment.