Skip to content

Commit

Permalink
fix: UHF-10903: Update application copy modal to use new dialog (#1563)
Browse files Browse the repository at this point in the history
* UHF-10903: New dialog form

* UHF-10903: Refactor application copy to custom controller.

* UHF-10903: Add custom selector option for dialogs.

* UHF-10903: Update tests for new copy functionality.

* UHF-10903: Rename method + 1 error

* UHF-10903: Fix typo

* UHF-10903: Refactor createDialog to support object argument.
  • Loading branch information
jiisuominen authored Nov 19, 2024
1 parent 02b60d3 commit 469a507
Show file tree
Hide file tree
Showing 15 changed files with 428 additions and 578 deletions.
25 changes: 20 additions & 5 deletions e2e/utils/copying_helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,22 +117,37 @@ const makeApplicationCopy = async (
logger(`Navigated to: ${viewPageURL}.`);

// Make sure we get there.
const applicationIdContainer = await page.locator('.webform-submission__application_id');
const applicationIdContainer = page.locator('.webform-submission__application_id');
const applicationIdContainerText = await applicationIdContainer.textContent();
expect(applicationIdContainerText).toContain(originalApplicationId);

// Make sure the application can be copied. If not, skip this test.
const isCopyApplicationButtonVisible = await page.locator('#copy-application-modal-form-link').isVisible();
if (!isCopyApplicationButtonVisible) {
const copyApplicationDialogButton = page.locator('#copy-application-button');
if (!await copyApplicationDialogButton.isVisible()) {
logger(`Copying disabled for application: ${originalApplicationId}. Skipping test.`);
test.skip(true, 'Skip copy test');
}

// Copy the original application.
logger(`Copying application: ${originalApplicationId}...`);

await page.locator('#copy-application-modal-form-link').click();
await page.locator('#copy-application-modal-form-submit').click();
// click the copy button
await copyApplicationDialogButton.click();

const dialogSelector = '.dialog.application-copy-dialog';
const actionButtonSelector = '#helfi-dialog__action-button';

// Wait for the dialog to appear
await page.waitForSelector(dialogSelector);

// Check if the dialog is visible
const isDialogVisible = await page.locator(dialogSelector).isVisible();

// Assert that the dialog is visible
expect(isDialogVisible).toBe(true);

// Click the action button within the specific dialog
await page.locator(`${dialogSelector} ${actionButtonSelector}`).click();

// Wait for a redirect to the new application and store the new application ID and submission URL.
await logCurrentUrl(page);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,11 @@ grants-dialog:
js/grants-dialog.js: { }
dependencies:
- core/drupal

application-copy-dialog:
js:
js/application-copy-dialog.js: {}
dependencies:
- core/jquery
- core/drupal
- core/drupalSettings
42 changes: 21 additions & 21 deletions public/modules/custom/grants_handler/grants_handler.module
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ use Drupal\Core\Url;
use Drupal\grants_attachments\AttachmentHandlerHelper;
use Drupal\grants_handler\ApplicationHelpers;
use Drupal\grants_handler\Event\UserLogoutEvent;
use Drupal\grants_handler\Form\CopyApplicationModalForm;
use Drupal\grants_handler\GrantsErrorStorage;
use Drupal\grants_handler\Helpers;
use Drupal\grants_handler\Plugin\WebformElement\CompensationsComposite;
Expand Down Expand Up @@ -1771,6 +1770,8 @@ function grants_handler_preprocess_webform_submission_information(array &$variab
$webform = $submission->getWebForm();
$thirdPartySettings = $webform->getThirdPartySettings('grants_metadata');

$applicationNumber = $submissionData['application_number'] ?? ApplicationHelpers::createApplicationNumber($submission);

$printApplicationUrl = Url::fromRoute(
'grants_webform_print.submission_print',
[
Expand Down Expand Up @@ -1836,38 +1837,36 @@ function grants_handler_preprocess_webform_submission_information(array &$variab
!$isApplicationArchived &&
$isApplicationOpen
) {

$copyApplicationUrl = Url::fromRoute(
'grants_handler.copy_application_modal',
'grants_handler.copy_application',
[
'submission_id' => $submissionData['application_number'],
'nojs' => 'ajax',
],
[
'attributes' => [
'class' => ['use-ajax', 'hds-button', 'hds-button--supplementary'],
'data-dialog-type' => 'modal',
'data-dialog-options' => json_encode(CopyApplicationModalForm::getDataDialogOptions()),
// Add this id so that we can test this form.
'id' => 'copy-application-modal-form-link',
],
]
);
$copyApplicationLinkText = [
'#theme' => 'edit-label-with-icon',
'#icon' => 'copy',
'#text_label' => t('Copy application', [], $tOpts),

// Attach the JavaScript file.
$variables['#attached']['library'][] = 'grants_handler/grants-dialog';
$variables['#attached']['library'][] = 'grants_handler/application-copy-dialog';

// Render a Twig template.
$html_content = \Drupal::service('twig')->render('themes/custom/hdbt_subtheme/templates/webform/application-copy-dialog-content.html.twig', [
'applicationNumber' => $applicationNumber,
'webformTitle' => $webform->label(),
]);

// Define the settings you want to pass.
$variables['#attached']['drupalSettings']['grants_handler'] = [
'copyUrl' => $copyApplicationUrl->toString(),
'htmlContent' => $html_content,
];
$copyApplicationLink = Link::fromTextAndUrl($copyApplicationLinkText, $copyApplicationUrl);
$variables['copyApplicationLink'] = $copyApplicationLink;
$variables['copyText'] = t('Copy application', [], $tOpts);
}

$variables['formTitle'] = $webform->label();
$variables['printLink'] = '';
$variables['copyLink'] = '';

$submissionData = $submission->getData();
$applicationNumber = $submissionData['application_number'] ?? ApplicationHelpers::createApplicationNumber($submission);

$variables['applicationNumber'] = $applicationNumber;
$variables['isEditable'] = $applicationStatusService->isSubmissionEditable($submission);
$variables['isEditPage'] = 'grants_handler.edit_application' === \Drupal::routeMatch()
Expand Down Expand Up @@ -1901,6 +1900,7 @@ function grants_handler_preprocess_webform_submission_information(array &$variab
'#theme' => 'webform_submission_attachment_list',
'#submission' => $submission,
];

}

/**
Expand Down
24 changes: 8 additions & 16 deletions public/modules/custom/grants_handler/grants_handler.routing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ grants_handler.view_application:
requirements:
_custom_access: '\Drupal\grants_handler\Controller\ApplicationController::accessByApplicationNumber'

grants_handler.copy_application:
path: '/hakemus/{submission_id}/kopioi'
defaults:
_title_callback: '\Drupal\grants_handler\Controller\ApplicationController::getTitle'
_controller: '\Drupal\grants_handler\Controller\CopyApplicationAjaxController'
requirements:
_custom_access: '\Drupal\grants_handler\Controller\ApplicationController::accessByApplicationNumber'

grants_handler.edit_application:
path: '/hakemus/{webform}/{webform_submission}/muokkaa'
defaults:
Expand All @@ -45,22 +53,6 @@ grants_handler.message_read:
requirements:
_custom_access: '\Drupal\grants_handler\Controller\ApplicationController::accessByApplicationNumber'

grants_handler.copy_application:
path: '/hakemus/{submission_id}/kopioi'
defaults:
_title: 'Copy application'
_form: 'Drupal\grants_handler\Form\CopyApplicationForm'
requirements:
_custom_access: '\Drupal\grants_handler\Controller\ApplicationController::accessByApplicationNumber'

grants_handler.copy_application_modal:
path: '/hakemus/{submission_id}/kopioi/{nojs}'
defaults:
_title: 'You are copying an application'
_form: 'Drupal\grants_handler\Form\CopyApplicationModalForm'
requirements:
_custom_access: '\Drupal\grants_handler\Controller\ApplicationController::accessByApplicationNumber'

grants_handler.atv_print_view:
path: '/hakemus/{submission_id}/tulosta'
defaults:
Expand Down
36 changes: 36 additions & 0 deletions public/modules/custom/grants_handler/js/application-copy-dialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
(function (Drupal) {
Drupal.behaviors.copyApplicationModalForm = {
attach: function (context, settings) {
const triggerButton = document.getElementById('copy-application-button');

triggerButton.addEventListener('click', function (event) {

event.preventDefault();

const htmlContent = drupalSettings.grants_handler.htmlContent;
const copyUrl = drupalSettings.grants_handler.copyUrl;

Drupal.dialogFunctions.createDialog({
dialogContent: htmlContent,
actionButtonText: Drupal.t('Copy application', [], { context: 'grants_handler' }),
backButtonText: Drupal.t('Close', [], { context: 'grants_handler' }),
closeButtonText: Drupal.t('Close', [], { context: 'grants_handler' }),
actionButtonCallback: () => {
// Redirect to a new URL
window.location.href = copyUrl;
/*
We probably should handle the whole copy process here, but for
now we just redirect to the copy URL.
Much better UX would be to show spinner here while copying
application and then redirect when it's ready
*/
},
dialogTitle: Drupal.t('Copy application', [], { context: 'grants_handler' }),
customSelector: 'application-copy-dialog'
})
});
},
};
})(Drupal);
77 changes: 54 additions & 23 deletions public/modules/custom/grants_handler/js/grants-dialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,51 @@
* closes the dialog.
* @param {Function} actionButtonCallback - The function to execute when
* the "action" button is clicked.
* @param dialogTitle
* If we want to override the default title
* @param {Function} closeButtonCallback
* The function to execute when the "close" button is clicked.
* @param {Function} backButtonCallback
* The function to execute when the "back" button is clicked.
* @param escapeButtonCallback
* The function to execute when the "escape" button is clicked.
* @param customSelector
* If we want to add a custom class to the dialog container
*/
createDialog: (dialogContent, actionButtonText, backButtonText, closeButtonText, actionButtonCallback = null) => {
const dialogTitle = Drupal.t('Attention', {}, { context: 'grants_handler' });
createDialog: ({
dialogContent,
actionButtonText,
backButtonText,
closeButtonText,
dialogTitle = Drupal.t('Attention', {}, { context: 'grants_handler' }),
actionButtonCallback = null,
closeButtonCallback = null,
backButtonCallback = null,
escapeButtonCallback = null,
customSelector = '',
}) => {
const actionButtonHTML = actionButtonText && `<button class="dialog__action-button" id="helfi-dialog__action-button" data-hds-component="button" data-hds-variant="primary">${actionButtonText}</button>`;
const backButtonHTML = backButtonText && `<button class="dialog__action-button" id="helfi-dialog__back-button" data-hds-component="button" data-hds-variant="secondary">${backButtonText}</button>`;
const closeButtonHTML = closeButtonText && `<button class="dialog__close-button" id="helfi-dialog__close-button"><span class="is-hidden">${closeButtonText}</span></button>`;

const dialogHTML = `
<div class="dialog__container" id="helfi-dialog__container">
<div class="dialog__overlay"></div>
<dialog class="dialog" id="helfi-dialog" aria-labelledby="helfi-dialog__title" aria-modal="true">
<div class="dialog__header">
${closeButtonHTML}
<h2 class="dialog__title" id="helfi-dialog__title">${dialogTitle}</h2>
</div>
<div class="dialog__content">
${dialogContent}
</div>
<div class="dialog__actions">
${actionButtonHTML}
${backButtonHTML}
</div>
</dialog>
<div class="dialog__container" id="helfi-dialog__container">
<div class="dialog__overlay"></div>
<dialog class="dialog ${customSelector}" id="helfi-dialog" aria-labelledby="helfi-dialog__title" aria-modal="true">
<div class="dialog__header">
${closeButtonHTML}
<h2 class="dialog__title" id="helfi-dialog__title">${dialogTitle}</h2>
</div>
`;
<div class="dialog__content">
${dialogContent}
</div>
<div class="dialog__actions">
${actionButtonHTML}
${backButtonHTML}
</div>
</dialog>
</div>
`;

// TODO: Surveys use very similar javascript dialog implementation.
// This and the survey implementation could possibly be merged with some
Expand All @@ -54,7 +74,7 @@
const closeButton = document.getElementById('helfi-dialog__close-button');
const dialog = document.getElementById('helfi-dialog__container');
const dialogFocusTrap = window.focusTrap.createFocusTrap('#helfi-dialog__container', {
initialFocus: () => '#helfi-dialog__title'
initialFocus: () => '#helfi-dialog__title',
});

// Activate the focus trap so that the user needs to react to the dialog.
Expand All @@ -68,18 +88,30 @@
// Add click event listener to back button
backButton.addEventListener('click', () => {
dialogFocusTrap.deactivate();
// If we have a callback, execute it.
if (backButtonCallback) {
backButtonCallback();
}
Drupal.dialogFunctions.removeDialog(dialog);
});

// Add click event listener to close button
closeButton.addEventListener('click', () => {
dialogFocusTrap.deactivate();
// If we have a callback, execute it.
if (closeButtonCallback) {
closeButtonCallback();
}
Drupal.dialogFunctions.removeDialog(dialog);
});

// Add event listener to ESC button to remove the dialog
document.body.addEventListener('keydown', function (event) {
if (event.key === 'Escape') {
// If we have a escapeButtonCallback, execute it also when pressing escape.
if (escapeButtonCallback) {
escapeButtonCallback();
}
Drupal.dialogFunctions.removeDialog(dialog);
}
});
Expand All @@ -90,8 +122,7 @@
document.body.style.paddingRight = `${
window.innerWidth - document.documentElement.clientWidth
}px`;
}
else {
} else {
document.body.style.removeProperty('padding-right');
}
},
Expand All @@ -105,6 +136,6 @@
dialog.remove();
Drupal.dialogFunctions.toggleNoScroll(false);
Drupal.dialogFunctions.setBodyPaddingRight(false);
}
}
},
};
})(Drupal);
Loading

0 comments on commit 469a507

Please sign in to comment.