Skip to content

Commit

Permalink
ChartLink Component rework.
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisMart21 committed Feb 14, 2024
1 parent ca5ad29 commit 9d8f929
Show file tree
Hide file tree
Showing 11 changed files with 229 additions and 883 deletions.
175 changes: 78 additions & 97 deletions src/client/app/components/ChartLinkComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,112 +3,93 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { Button } from 'reactstrap';
import { ChartTypes } from '../types/redux/graph';
import { getRangeSliderInterval } from './LineChartComponent';
import { toast } from 'react-toastify';
import ReactTooltip from 'react-tooltip';
import { Button, ButtonGroup, Input } from 'reactstrap';
import { useAppDispatch, useAppSelector } from '../redux/reduxHooks';
import { selectChartLink } from '../redux/selectors/uiSelectors';
import { selectChartLinkHideOptions, setChartLinkOptionsVisibility } from '../redux/slices/appStateSlice';
import { selectSelectedGroups, selectSelectedMeters } from '../redux/slices/graphSlice';
import { showErrorNotification, showInfoNotification } from '../utils/notifications';
import TooltipMarkerComponent from './TooltipMarkerComponent';

interface ChartLinkProps {
linkText: string;
chartType: ChartTypes;
}

interface ChartLinkState {
showLink: boolean;
showSliderRange: boolean;
sliderRange: string;
showOptionalLink: boolean;
optionalLink: string;
hideOptionsInLinkedPage: boolean;
}
/**
* @returns chartLinkComponent
*/
export default function ChartLinkComponent() {
const dispatch = useAppDispatch()
const [linkTextVisible, setLinkTextVisible] = React.useState<boolean>(false);
const linkText = useAppSelector(selectChartLink)
const linkHideOptions = useAppSelector(selectChartLinkHideOptions)
const selectedMeters = useAppSelector(selectSelectedMeters)
const selectedGroups = useAppSelector(selectSelectedGroups)
const ref = React.useRef<HTMLDivElement>(null)
const handleButtonClick = () => {
// First attempt to write directly to user's clipboard.
navigator.clipboard.writeText(linkText)
.then(() => {
showInfoNotification('Copied To Clipboard', toast.POSITION.TOP_RIGHT, 1000)
})
.catch(() => {
// if operation fails, open copyable text for manual copy.
showErrorNotification('Failed to Copied To Clipboard!', toast.POSITION.TOP_RIGHT, 1000)
setLinkTextVisible(true)
})

export default class ChartLinkComponent extends React.Component<ChartLinkProps, ChartLinkState> {
constructor(props: ChartLinkProps) {
super(props);
this.toggleLink = this.toggleLink.bind(this);
this.handleOptionsVisibility = this.handleOptionsVisibility.bind(this);
this.state = {
showLink: false,
showSliderRange: false,
sliderRange: '',
showOptionalLink: true,
optionalLink: '',
hideOptionsInLinkedPage: false
};
}

public render() {
const wellStyle: React.CSSProperties = {
wordWrap: 'break-word',
padding: '9px',
minHeight: '20px',
marginBottom: '20px',
backgroundColor: '#f5f5f5',
border: '1px solid #e3e3e3'
}
if (selectedMeters.length > 0 || selectedGroups.length > 0) {

};
return (
<div>
<Button outline onClick={this.toggleLink}>
<FormattedMessage id='toggle.link' />
</Button>
<TooltipMarkerComponent page='home' helpTextId='help.home.toggle.chart.link' />
{this.state.showLink &&
<>
<div className='checkbox'>
<label><input type='checkbox' onChange={this.handleOptionsVisibility} checked={this.state.hideOptionsInLinkedPage} />
<FormattedMessage id='hide.options.in.link' />
</label>
</div>
<div style={wellStyle}>
{this.props.linkText}
{this.state.showSliderRange && this.state.sliderRange}
{this.state.showOptionalLink && this.state.optionalLink}
</div>
</>
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'start' }}>
<ButtonGroup >
{/* TODO translation needed */}
<Button outline onClick={handleButtonClick} >
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-evenly', gap: '1em', alignItems: 'center' }}>
Chart Link
<div ref={ref} data-for={'home'} data-tip={'help.home.toggle.chart.link'} >
<Input type='checkbox' defaultChecked={linkHideOptions}
onClickCapture={e => {
e.stopPropagation()
dispatch(setChartLinkOptionsVisibility(!linkHideOptions))
}}
onMouseOver={() => {
ref.current && ReactTooltip.show(ref.current)
}}
onMouseLeave={() => {
ref.current && ReactTooltip.hide(ref.current)
}}
/>
</div>
</div>
</Button>
<Button outline onClick={() => setLinkTextVisible(visible => !visible)}>
{linkTextVisible ? 'x' : 'v'}
</Button>
</ButtonGroup>
<TooltipMarkerComponent page='home' helpTextId='help.home.toggle.chart.link' />
</div>
{
linkTextVisible &&
<div style={wellStyle}>
{linkText}
</div>
}
</div>
);
}

private toggleLink() {
if (this.state.showLink) {
this.setState({
showLink: false,
showSliderRange: false
});
} else {
if (this.props.chartType === 'line') {
const newSliderRange = this.getSliderRangeString(); // get sliderRange on demand;
this.setState({
showLink: !this.state.showLink,
showSliderRange: true,
sliderRange: newSliderRange
});
} else {
this.setState({
showLink: !this.state.showLink
});
}
}
</div >
)
}

private getSliderRangeString() {
const sliderRangeString = `&sliderRange=${getRangeSliderInterval()}`;
return sliderRangeString;
}

/**
* TODO: this could be refactor into part of an interface that holds all user-selected options
* and produce output as a single string by iterating over a loop.
*/
private handleOptionsVisibility() {
const currState = this.state.hideOptionsInLinkedPage;
const optionsVisibilityToken = '&optionsVisibility=false';
this.setState({
hideOptionsInLinkedPage: !currState,
optionalLink: (currState) ? '' : optionsVisibilityToken
});
else {
return null
}
}

const wellStyle: React.CSSProperties = {
wordWrap: 'break-word',
padding: '9px',
minHeight: '20px',
marginBottom: '20px',
backgroundColor: '#f5f5f5',
border: '1px solid #e3e3e3'
};
40 changes: 0 additions & 40 deletions src/client/app/components/LineChartComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,43 +110,3 @@ export default function LineChartComponent() {

}
}


/**
* Determines the line graph's slider interval based after the slider is moved
* @returns The slider interval, either 'all' or a TimeInterval
*/
export function getRangeSliderInterval(): string {
const sliderContainer: any = document.querySelector('.rangeslider-bg');
const sliderBox: any = document.querySelector('.rangeslider-slidebox');
const root: any = document.getElementById('root');

if (sliderContainer && sliderBox && root) {
// Attributes of the slider: full width and the min & max values of the box
const fullWidth: number = parseInt(sliderContainer.getAttribute('width'));
const sliderMinX: number = parseInt(sliderBox.getAttribute('x'));
const sliderMaxX: number = sliderMinX + parseInt(sliderBox.getAttribute('width'));
if (sliderMaxX - sliderMinX === fullWidth) {
return 'all';
}

// From the Plotly line graph, get current min and max times in seconds
const minTimeStamp: number = parseInt(root.getAttribute('min-timestamp'));
const maxTimeStamp: number = parseInt(root.getAttribute('max-timestamp'));

// Seconds displayed on graph
const deltaSeconds: number = maxTimeStamp - minTimeStamp;
const secondsPerPixel: number = deltaSeconds / fullWidth;

// Get the new min and max times, in seconds, from the slider box
const newMinXTimestamp = Math.floor(minTimeStamp + (secondsPerPixel * sliderMinX));
const newMaxXTimestamp = Math.floor(minTimeStamp + (secondsPerPixel * sliderMaxX));
// The newMin/MaxTimestamp is equivalent to a Unix time in milliseconds. Thus, it will
// shift with timezone. It isn't clear if we want it in local or UTC. It depends on what
// plotly does. Here it is assumed that local is what is desired. This seems to work
// and not shift the graphs x-axis so using.
return new TimeInterval(moment(newMinXTimestamp), moment(newMaxXTimestamp)).toString();
} else {
throw new Error('unable to get range slider params');
}
}
4 changes: 2 additions & 2 deletions src/client/app/components/MeterAndGroupSelectComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ import ReactTooltip from 'react-tooltip';
import { Badge } from 'reactstrap';
import { graphSlice } from '../redux/slices/graphSlice';
import { useAppDispatch, useAppSelector } from '../redux/reduxHooks';
import { selectAnythingFetching, selectMeterGroupSelectData } from '../redux/selectors/uiSelectors';
import { selectMeterGroupSelectData } from '../redux/selectors/uiSelectors';
import { GroupedOption, SelectOption } from '../types/items';
import { MeterOrGroup } from '../types/redux/graph';
import translate from '../utils/translate';
import TooltipMarkerComponent from './TooltipMarkerComponent';

import { selectAnythingFetching } from '../redux/selectors/uiSelectors';
/**
* Creates a React-Select component for the UI Options Panel.
* @param props - Helps differentiate between meter or group options
Expand Down
4 changes: 2 additions & 2 deletions src/client/app/components/UIOptionsComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import * as React from 'react';
import ReactTooltip from 'react-tooltip';
import ExportComponent from '../components/ExportComponent';
import ChartLinkContainer from '../containers/ChartLinkContainer';
import { selectChartToRender } from '../redux/slices/graphSlice';
import { useAppSelector } from '../redux/reduxHooks';
import { ChartTypes } from '../types/redux/graph';
Expand All @@ -19,6 +18,7 @@ import GraphicRateMenuComponent from './GraphicRateMenuComponent';
import MapControlsComponent from './MapControlsComponent';
import ThreeDSelectComponent from './ReadingsPerDaySelectComponent';
import ChartDataSelectComponent from './ChartDataSelectComponent';
import ChartLinkComponent from './ChartLinkComponent';

/**
* @returns the UI Control panel
Expand Down Expand Up @@ -81,7 +81,7 @@ export default function UIOptionsComponent() {
chartToRender !== ChartTypes.threeD &&
chartToRender !== ChartTypes.radar && <ExportComponent />
}
<ChartLinkContainer />
<ChartLinkComponent />
</div>
);
}
80 changes: 0 additions & 80 deletions src/client/app/containers/ChartLinkContainer.ts

This file was deleted.

Loading

0 comments on commit 9d8f929

Please sign in to comment.