Injecting code into UT Pages - Longhorn-Developers/UT-Registration-Plus GitHub Wiki
This page documents how we inject code into supported pages across the UT Austin web ecosystem.
Current URL is obtained through window.location.href
by calling getSiteSupport
Contains enum to determine what page a user is on. For example
if (url.includes('my.utexas.edu/student/student/index') || url.includes('my.utexas.edu/student/')) {
return SiteSupport.MY_UT;
}
Found in index.tsx
in pages
, the renderComponent
function is a utility used by the extension to inject React components into websites (and other supported pages). It creates a new div and appends it to the document body. It then uses React’s createRoot to render the React component inside this container.
const renderComponent = (Component: React.ComponentType) => {
const container = document.createElement('div');
container.id = 'extension-root';
document.body.appendChild(container);
createRoot(container).render(
<React.StrictMode>
<Component />
</React.StrictMode>
);
};
After a site is determined, components are rendered in index.tsx
. For example
if (support === SiteSupport.COURSE_CATALOG_DETAILS || support === SiteSupport.COURSE_CATALOG_LIST) {
renderComponent(() => <CourseCatalogMain support={support} />);
}
if (support === SiteSupport.MY_UT) {
renderComponent(InjectedButton);
}
if (support === SiteSupport.COURSE_CATALOG_SEARCH) {
renderComponent(DaysCheckbox);
}
This can be accomplished easily through retrieving elements, for example using document.getElementById
, and changing their innerHTML
to ''
For example with DaysCheckbox
const daysDropdown = document.getElementById('mtg_days_st') as HTMLSelectElement | null;
if (!daysDropdown) {
console.error('Days dropdown not found');
return;
}
const formElement = daysDropdown.closest('.form_element')!;
const checkboxContainer = document.createElement('div');
// Create a hidden input to store the value
const hiddenInput = document.createElement('input');
hiddenInput.type = 'hidden';
hiddenInput.name = 'mtg_days_st';
hiddenInput.id = 'mtg_days_st_hidden';
hiddenInput.value = daysDropdown.value;
// Remove old dropdown
formElement.innerHTML = '';
Relevant page:
<ExtensionRoot>
used to prevent the native page styles from being overridden with our own.
return (
<ExtensionRoot>
<NewSearchLink />
<RecruitmentBanner />
<TableHead>Plus</TableHead>
{rows.map(
row =>
row.course && (
<TableRow
key={row.course.uniqueId}
row={row}
isSelected={row.course.uniqueId === selectedCourse?.uniqueId}
activeSchedule={activeSchedule}
onClick={handleRowButtonClick(row.course)}
/>
)
)}
<CourseCatalogInjectedPopup
course={selectedCourse!} // always defined when showPopup is true
show={showPopup}
onClose={() => setShowPopup(false)}
afterLeave={() => setSelectedCourse(null)}
/>
{enableScrollToLoad && <AutoLoad addRows={addRows} />}
</ExtensionRoot>
);
// Existing code above
return ReactDOM.createPortal(
<ExtensionRoot>
<Button variant='filled' color='ut-burntorange' onClick={extractCoursesFromCalendar}>
Add Courses to UT Registration+
</Button>
</ExtensionRoot>,
container
);