feat: add tooltip for Other in grade distribution (#709)
* feat: add tooltip * refactor: lint happy * chore: lint --------- Co-authored-by: Derek Chen <derex1987@gmail.com>
This commit is contained in:
44
src/views/components/common/Tooltip.tsx
Normal file
44
src/views/components/common/Tooltip.tsx
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import clsx from 'clsx';
|
||||||
|
import type { PropsWithChildren, ReactNode } from 'react';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface TooltipProps {
|
||||||
|
className?: string;
|
||||||
|
contentClassName?: string;
|
||||||
|
content: ReactNode;
|
||||||
|
offsetX: number;
|
||||||
|
offsetY: number;
|
||||||
|
maxWidth?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tooltip that displays content on hover
|
||||||
|
*/
|
||||||
|
export default function Tooltip({
|
||||||
|
className,
|
||||||
|
contentClassName,
|
||||||
|
content,
|
||||||
|
offsetX,
|
||||||
|
offsetY,
|
||||||
|
maxWidth,
|
||||||
|
children,
|
||||||
|
}: PropsWithChildren<TooltipProps>): JSX.Element {
|
||||||
|
return (
|
||||||
|
<span className={clsx('relative inline-flex group', className)}>
|
||||||
|
{children}
|
||||||
|
<span
|
||||||
|
className={clsx(
|
||||||
|
'pointer-events-none absolute rounded-md bg-white px-3 py-2 text-xs invisible opacity-0 transition-opacity group-hover:visible group-hover:opacity-100 whitespace-normal break-words',
|
||||||
|
contentClassName
|
||||||
|
)}
|
||||||
|
style={{
|
||||||
|
marginTop: offsetY,
|
||||||
|
marginLeft: offsetX,
|
||||||
|
maxWidth,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import type { Distribution, LetterGrade } from '@shared/types/Distribution';
|
|||||||
import { extendedColors } from '@shared/types/ThemeColors';
|
import { extendedColors } from '@shared/types/ThemeColors';
|
||||||
import Link from '@views/components/common/Link';
|
import Link from '@views/components/common/Link';
|
||||||
import Text from '@views/components/common/Text/Text';
|
import Text from '@views/components/common/Text/Text';
|
||||||
|
import Tooltip from '@views/components/common/Tooltip';
|
||||||
import {
|
import {
|
||||||
NoDataError,
|
NoDataError,
|
||||||
queryAggregateDistribution,
|
queryAggregateDistribution,
|
||||||
@@ -12,10 +13,12 @@ import Highcharts from 'highcharts';
|
|||||||
import HighchartsReact from 'highcharts-react-official';
|
import HighchartsReact from 'highcharts-react-official';
|
||||||
import type { ChangeEvent } from 'react';
|
import type { ChangeEvent } from 'react';
|
||||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
import { renderToStaticMarkup } from 'react-dom/server';
|
||||||
import Skeleton from 'react-loading-skeleton';
|
import Skeleton from 'react-loading-skeleton';
|
||||||
|
|
||||||
const UT_GRADE_DISTRIBUTION_URL = 'https://reports.utexas.edu/spotlight-data/ut-course-grade-distributions';
|
const UT_GRADE_DISTRIBUTION_URL = 'https://reports.utexas.edu/spotlight-data/ut-course-grade-distributions';
|
||||||
|
const TOOLTIP_CONTENT =
|
||||||
|
"The 'Other' grade category includes all non-standard letter grades, including: In Progress, Incomplete, Permanent Incomplete, Oblit, Q-Drop, Withdrawn, Credit, No Credit, Satisfactory, Unsatisfactory, and Registered on CR/F or CR/NC basis.";
|
||||||
interface GradeDistributionProps {
|
interface GradeDistributionProps {
|
||||||
course: Course;
|
course: Course;
|
||||||
}
|
}
|
||||||
@@ -126,6 +129,25 @@ export default function GradeDistribution({ course }: GradeDistributionProps): J
|
|||||||
lineHeight: 'normal',
|
lineHeight: 'normal',
|
||||||
fontStyle: 'normal',
|
fontStyle: 'normal',
|
||||||
},
|
},
|
||||||
|
useHTML: true,
|
||||||
|
formatter() {
|
||||||
|
// eslint-disable-next-line react/no-this-in-sfc
|
||||||
|
const val = `${this.value}`;
|
||||||
|
|
||||||
|
return val === 'Other'
|
||||||
|
? renderToStaticMarkup(
|
||||||
|
<Tooltip
|
||||||
|
content={TOOLTIP_CONTENT}
|
||||||
|
className='underline'
|
||||||
|
offsetX={-425}
|
||||||
|
offsetY={-175}
|
||||||
|
maxWidth={500}
|
||||||
|
>
|
||||||
|
Other
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
: val;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
text: 'Grades',
|
text: 'Grades',
|
||||||
@@ -135,6 +157,7 @@ export default function GradeDistribution({ course }: GradeDistributionProps): J
|
|||||||
fontWeight: '400',
|
fontWeight: '400',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
categories: ['A', 'A-', 'B+', 'B', 'B-', 'C+', 'C', 'C-', 'D+', 'D', 'D-', 'F', 'Other'],
|
categories: ['A', 'A-', 'B+', 'B', 'B-', 'C+', 'C', 'C-', 'D+', 'D', 'D-', 'F', 'Other'],
|
||||||
tickInterval: 1,
|
tickInterval: 1,
|
||||||
tickWidth: 1,
|
tickWidth: 1,
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import React from 'react';
|
|||||||
* Lightweight skeleton placeholder for contributor cards while data loads
|
* Lightweight skeleton placeholder for contributor cards while data loads
|
||||||
*/
|
*/
|
||||||
export const ContributorCardSkeleton: React.FC = () => (
|
export const ContributorCardSkeleton: React.FC = () => (
|
||||||
<div className='border border-gray-300 rounded bg-ut-gray/10 p-4 animate-pulse'>
|
<div className='animate-pulse border border-gray-300 rounded bg-ut-gray/10 p-4'>
|
||||||
<div className='h-4 w-3/4 bg-gray-300 rounded mb-2' />
|
<div className='mb-2 h-4 w-3/4 rounded bg-gray-300' />
|
||||||
<div className='h-3 w-1/2 bg-gray-300 rounded mb-1' />
|
<div className='mb-1 h-3 w-1/2 rounded bg-gray-300' />
|
||||||
<div className='h-3 w-1/4 bg-gray-300 rounded' />
|
<div className='h-3 w-1/4 rounded bg-gray-300' />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user