DPIA Questionnaire

Interactive questionnaire for Data Protection Impact Assessments

Overview

The DPIA Questionnaire component provides an interactive form for conducting Data Protection Impact Assessments (DPIAs) in compliance with the Nigeria Data Protection Act 2023 (NDPA). This component helps organizations identify and minimize data protection risks in their projects or systems.

When to use a DPIA

Under the NDPA 2023, a DPIA is required when processing is likely to result in a high risk to the rights and freedoms of individuals. This includes systematic and extensive profiling, processing of special categories of data on a large scale, or systematic monitoring of public areas.

Installation

Install the NDPR Toolkit package which includes the DPIA Questionnaire component:

pnpm add @tantainnovative/ndpr-toolkit

Usage

Import and use the DPIAQuestionnaire component in your React application:

import { DPIAQuestionnaire } from '@tantainnovative/ndpr-toolkit';
import { useState } from 'react';

const sections = [
  {
    id: 'data-collection',
    title: 'Data Collection',
    description: 'Tell us about the data being processed.',
    order: 1,
    questions: [
      {
        id: 'data-types',
        text: 'What types of personal data will be collected?',
        type: 'radio',
        required: true,
        options: [
          { value: 'basic', label: 'Basic contact information only', riskLevel: 'low' },
          { value: 'identifiers', label: 'Personal identifiers and contact information', riskLevel: 'medium' },
          { value: 'sensitive', label: 'Sensitive personal data (health, biometric, etc.)', riskLevel: 'high' }
        ]
      },
      {
        id: 'data-volume',
        text: 'What is the volume of data being processed?',
        type: 'radio',
        required: true,
        options: [
          { value: 'small', label: 'Small scale (fewer than 100 data subjects)', riskLevel: 'low' },
          { value: 'medium', label: 'Medium scale (100-1000 data subjects)', riskLevel: 'medium' },
          { value: 'large', label: 'Large scale (more than 1000 data subjects)', riskLevel: 'high' }
        ]
      }
    ]
  }
];

function MyDPIAForm() {
  const [answers, setAnswers] = useState({});
  const [currentSectionIndex, setCurrentSectionIndex] = useState(0);

  return (
    <div>
      <h1>Data Protection Impact Assessment</h1>
      <DPIAQuestionnaire
        sections={sections}
        answers={answers}
        onAnswerChange={(questionId, value) =>
          setAnswers((prev) => ({ ...prev, [questionId]: value }))
        }
        currentSectionIndex={currentSectionIndex}
        onNextSection={() => setCurrentSectionIndex((i) => i + 1)}
        onPrevSection={() => setCurrentSectionIndex((i) => i - 1)}
      />
    </div>
  );
}

Props

NameTypeRequiredDescription
sectionsDPIASection[]YesSections of the DPIA questionnaire
answersRecord<string, string | number | boolean | string[]>YesCurrent answers to the questionnaire, keyed by question id
onAnswerChange(questionId: string, value: string | number | boolean | string[]) => voidYesCalled when an answer is updated
currentSectionIndexnumberYesIndex of the currently displayed section
onNextSection() => voidNoCalled when the user navigates to the next section
onPrevSection() => voidNoCalled when the user navigates to the previous section
validationErrorsRecord<string, string>NoValidation errors for the current section, keyed by question id
readOnlybooleanNoWhether the questionnaire is in read-only mode (default false)
showProgressbooleanNoWhether to show a progress indicator (default true)
progressnumberNoCurrent progress percentage (0-100)
nextButtonTextstringNoText for the next button (default "Next")
prevButtonTextstringNoText for the previous button (default "Previous")
submitButtonTextstringNoText for the submit button shown on the last section (default "Submit")
classNamestringNoCustom CSS class for the questionnaire
buttonClassNamestringNoCustom CSS class for the buttons
classNamesDPIAQuestionnaireClassNamesNoPer-section class name overrides
unstyledbooleanNoWhen true, strips all default classes; only classNames overrides apply (default false)

See the API Reference below for the full DPIAQuestion and DPIASection shapes.

Examples

Basic Example

import { DPIAQuestionnaire } from '@tantainnovative/ndpr-toolkit';
import { useState } from 'react';

const basicSections = [
  {
    id: 'overview',
    title: 'Project Overview',
    order: 1,
    questions: [
      {
        id: 'q1',
        text: 'Does the project involve collecting personal data?',
        type: 'radio',
        required: true,
        options: [
          { value: 'none', label: 'No personal data collected', riskLevel: 'low' },
          { value: 'limited', label: 'Limited personal data collected', riskLevel: 'medium' },
          { value: 'extensive', label: 'Extensive personal data collected', riskLevel: 'high' }
        ]
      },
      {
        id: 'q2',
        text: 'Will the data be shared with third parties?',
        type: 'radio',
        required: true,
        options: [
          { value: 'none', label: 'No sharing with third parties', riskLevel: 'low' },
          { value: 'limited', label: 'Limited sharing with trusted partners', riskLevel: 'medium' },
          { value: 'extensive', label: 'Extensive sharing with multiple third parties', riskLevel: 'high' }
        ]
      }
    ]
  }
];

function BasicDPIA() {
  const [answers, setAnswers] = useState({});

  return (
    <DPIAQuestionnaire
      sections={basicSections}
      answers={answers}
      onAnswerChange={(questionId, value) =>
        setAnswers((prev) => ({ ...prev, [questionId]: value }))
      }
      currentSectionIndex={0}
    />
  );
}

With Initial Values

import { DPIAQuestionnaire } from '@tantainnovative/ndpr-toolkit';
import { useState } from 'react';

function DPIAWithInitialValues() {
  // The questionnaire is fully controlled — seed the answers state
  // with whatever values you have already collected.
  const [answers, setAnswers] = useState({ q1: 'limited', q2: 'none' });

  return (
    <DPIAQuestionnaire
      sections={basicSections}
      answers={answers}
      onAnswerChange={(questionId, value) =>
        setAnswers((prev) => ({ ...prev, [questionId]: value }))
      }
      currentSectionIndex={0}
    />
  );
}

Multi-Section Navigation & Validation

import { DPIAQuestionnaire } from '@tantainnovative/ndpr-toolkit';
import { useState } from 'react';

function MultiSectionDPIA({ sections }) {
  const [answers, setAnswers] = useState({});
  const [currentSectionIndex, setCurrentSectionIndex] = useState(0);
  const [validationErrors, setValidationErrors] = useState({});

  const validateSection = () => {
    const section = sections[currentSectionIndex];
    const errors = {};
    section.questions.forEach((q) => {
      if (q.required && !answers[q.id]) {
        errors[q.id] = 'This question is required';
      }
    });
    setValidationErrors(errors);
    return Object.keys(errors).length === 0;
  };

  const handleNext = () => {
    if (!validateSection()) return;
    if (currentSectionIndex < sections.length - 1) {
      setCurrentSectionIndex((i) => i + 1);
    } else {
      console.log('Submitted answers:', answers);
    }
  };

  return (
    <DPIAQuestionnaire
      sections={sections}
      answers={answers}
      onAnswerChange={(questionId, value) =>
        setAnswers((prev) => ({ ...prev, [questionId]: value }))
      }
      currentSectionIndex={currentSectionIndex}
      onNextSection={handleNext}
      onPrevSection={() => setCurrentSectionIndex((i) => i - 1)}
      validationErrors={validationErrors}
      progress={Math.round(((currentSectionIndex + 1) / sections.length) * 100)}
    />
  );
}

Displaying the Report

Once a DPIA is complete, render the results with the DPIAReport component. It takes a result (a DPIAResult) plus the same sections used in the questionnaire:

import { DPIAReport } from '@tantainnovative/ndpr-toolkit';

function DPIAResultView({ result, sections }) {
  return (
    <DPIAReport
      result={result}
      sections={sections}
      showFullReport
      allowPrint
      allowExport
      onExport={(format) => console.log('Exporting as', format)}
    />
  );
}

API Reference

DPIAQuestion Interface

export interface DPIAQuestion {
  id: string;
  text: string;
  guidance?: string;
  type: 'text' | 'textarea' | 'select' | 'radio' | 'checkbox' | 'scale';
  options?: Array<{
    value: string;
    label: string;
    riskLevel?: 'low' | 'medium' | 'high';
  }>;
  minValue?: number;
  maxValue?: number;
  scaleLabels?: Record<number, string>;
  required: boolean;
  riskLevel?: 'low' | 'medium' | 'high';
  hasDependentQuestions?: boolean;
  showWhen?: Array<{
    questionId: string;
    operator: 'equals' | 'contains' | 'greaterThan' | 'lessThan';
    value: string | number | boolean;
  }>;
}

DPIASection Interface

export interface DPIASection {
  id: string;
  title: string;
  description?: string;
  questions: DPIAQuestion[];
  order: number;
}

DPIARisk Interface

export interface DPIARisk {
  id: string;
  description: string;
  likelihood: number;
  impact: number;
  score: number;
  level: 'low' | 'medium' | 'high' | 'critical';
  mitigationMeasures?: string[];
  mitigated: boolean;
  residualScore?: number;
  relatedQuestionIds: string[];
}

DPIAResult Interface

export interface DPIAResult {
  id: string;
  title: string;
  processingDescription: string;
  startedAt: number;
  completedAt?: number;
  assessor: {
    name: string;
    role: string;
    email: string;
  };
  answers: Record<string, string | number | boolean | string[]>;
  risks: DPIARisk[];
  overallRiskLevel: 'low' | 'medium' | 'high' | 'critical';
  canProceed: boolean;
  conclusion: string;
  recommendations?: string[];
  reviewDate?: number;
  version: string;
  ndpcConsultationRequired?: boolean;
  ndpcConsultationDate?: number;
  ndpcConsultationReference?: string;
  lawfulBasis?: string;
  involvesCrossBorderTransfer?: boolean;
}

DPIAReport Props

NameTypeRequiredDescription
resultDPIAResultYesThe DPIA result to display
sectionsDPIASection[]YesThe sections of the DPIA questionnaire (used to label questions)
showFullReportbooleanNoWhether to show the full report or just a summary (default true)
allowPrintbooleanNoWhether to allow printing the report (default true)
allowExportbooleanNoWhether to allow exporting the report as PDF (default true)
onExport(format: 'pdf' | 'docx' | 'html') => voidNoCalled when the report is exported
classNamestringNoCustom CSS class for the report container
buttonClassNamestringNoCustom CSS class for the buttons
classNamesDPIAReportClassNamesNoPer-section class name overrides
unstyledbooleanNoWhen true, strips all default classes; only classNames overrides apply (default false)

useDPIA Hook

import { useDPIA } from '@tantainnovative/ndpr-toolkit';

const {
  dpias,          // Array of all DPIAs
  startDPIA,      // Function to start a new DPIA
  updateDPIA,     // Function to update an existing DPIA
  completeDPIA,   // Function to complete a DPIA
  getDPIAById,    // Function to get a DPIA by ID
  identifyRisks,  // Function to identify risks based on answers
  assessRisk,     // Function to assess a specific risk
  generateReport  // Function to generate a DPIA report
} = useDPIA();

// Start a new DPIA
const newDPIA = startDPIA({
  title: 'Customer Data Analytics Platform',
  processingDescription: 'Processing customer data for analytics and personalization',
  assessor: {
    name: 'Jane Smith',
    role: 'Data Protection Officer',
    email: 'jane@example.com'
  }
});

// Update DPIA with answers
updateDPIA(newDPIA.id, {
  answers: {
    'data-types': ['personal', 'behavioral'],
    'data-volume': 'large',
    'automated-processing': true,
    'sensitive-data': false
  }
});

// Identify risks and complete
const risks = identifyRisks(newDPIA.id);
completeDPIA(newDPIA.id, {
  risks,
  overallRiskLevel: 'medium',
  canProceed: true,
  recommendations: [
    'Implement data minimization practices',
    'Enhance access controls',
    'Conduct regular security audits'
  ]
});

Best Practices

  • Comprehensive Questions: Include questions that cover all aspects of data processing, including collection, storage, sharing, and deletion.
  • Risk Scoring: Implement a risk scoring system to help identify high-risk areas that need additional mitigation measures.
  • Documentation: Ensure the DPIA results are documented and stored for compliance purposes. The NDPA requires organizations to maintain records of processing activities.
  • Regular Reviews: DPIAs should be reviewed periodically, especially when there are changes to the processing activities.
  • Consultation: For high-risk processing, consider consulting with the NDPC or a data protection expert.

Accessibility

The DPIAQuestionnaire component is built with accessibility in mind:

  • All form elements have proper labels and ARIA attributes
  • Focus states are clearly visible
  • Color contrast meets WCAG 2.1 AA standards
  • Keyboard navigation is fully supported

To ensure maximum accessibility, make sure to provide clear and descriptive question text and help text where appropriate.

Need Help?

If you have questions about implementing the DPIA Questionnaire or need assistance with NDPA compliance, check out these resources:

GitHub Issues

Report bugs or request features on our GitHub repository.

View Issues

NDPA Resources

Learn more about NDPA 2023 compliance requirements.

NDPA Framework